Warm-up
- We now set common parameters as new variables, once and for all for this
session :
# setparam
## Set your project name
# WARNING : Do not just copy-paste this ! It's MY project name ! Put YOURS !!
project_name <- "ebaii_sc_teachers"
## Control if the project_name exists on the cluster
cat('PATH CHECK : ', dir.exists(paste0('/shared/projects/', project_name)))
Show output
PATH CHECK : TRUE
## Seed for the RNG
my_seed <- 1337L
# ## Empty droplets max p-value
# max_p <- 1E-03
Prepare the data structure [PREPROC.2]
Main directory
# maindir
## Preparing the path
TD_dir <- paste0("/shared/projects/", project_name, "/SC_TD")
## Creating the root directory
# dir.create(path = TD_dir, recursive = TRUE)
## Print the root directory on-screen
print(TD_dir)
Show output
[1] "/shared/projects/ebaii_sc_teachers/SC_TD"
Current session
# sessiondir
## Creating the session (Preproc.2) directory
session_dir <- paste0(TD_dir, "/02_Preproc.2")
# dir.create(path = session_dir, recursive = TRUE)
## Print the session directory on-screen
print(session_dir)
Show output
[1] "/shared/projects/ebaii_sc_teachers/SC_TD/02_Preproc.2"
Genelists directory
This is a directory where we will store additional information from
knowledge bases about genes used to estimate the cell cycle phase of cells.
# resdir
res_dir <- paste0(TD_dir, "/Resources")
glist_dir <- paste0(res_dir, "/Genelists")
## Create the directory
# dir.create(path = glist_dir, recursive = TRUE)
## Print the resources directory on-screen
print(glist_dir)
Show output
[1] "/shared/projects/ebaii_sc_teachers/SC_TD/Resources/Genelists"
Output directory
# outdir
## Creating the OUTPUT data directory
output_dir <- paste0(session_dir, "/RESULTS")
# dir.create(path = output_dir, recursive = TRUE)
## Print the output directory on-screen
print(output_dir)
Show output
[1] "/shared/projects/ebaii_sc_teachers/SC_TD/02_Preproc.2/RESULTS"
Load the raw matrix [PreProc.2]
We retrieve the input data file
# mat_dl
local <- FALSE
## The raw count matrix we will start from
scmat_source <- "GSM4861194_gex_2_raw_gene_expression.tsv.gz"
## Download the file from Zenodo
if (!local) {
### ZenID
zen_id <- "14033941"
### Zen Path
zen_backup_file <- paste0("https://zenodo.org/records/",
zen_id,
"/files/",
scmat_source)
## The path to the locally saved input file
scmat_file <- paste0(input_dir,
'/',
scmat_source)
## Download the file
download.file(url = zen_backup_file,
destfile = scmat_file)
} else {
ebaii_session <- '2538_eb3i_n1_2025'
scmat_file <- paste0(
'/shared/projects/',
ebaii_session,
'/atelier_scrnaseq/TD/BACKUP/TSV/',
scmat_source)
}
We can load it into R :
# mat_load
scmat <- as.matrix(
utils::read.table(
file = scmat_file,
header = TRUE,
sep = "\t"))
## Displaying its size in-memory (this is a basic matrix)
format(utils::object.size(scmat), units = "auto")
Show output
[1] "545.7 Mb"
Load the genelists resources [PreProc.2]
We retrieve the genelists
# gl_dl
local <- FALSE
## The genelist files
mito_source <- "mus_musculus_mito_symbols_20191015.rds"
ribo_source <- "mus_musculus_cribo_symbols_20191015.rds"
stress_source <- "mus_musculus_stress_symbols_20200224.rds"
gl_sources <- c(mito_source, ribo_source, stress_source)
## The (future) local files
mito_file <- paste0(input_dir, '/', mito_source)
ribo_file <- paste0(input_dir, '/', ribo_source)
stress_file <- paste0(input_dir, '/', stress_source)
gl_files <- c(mito_file, ribo_file, stress_file)
## Download the file from Zenodo
if (!local) {
### ZenID
zen_id <- "14037355"
### Looping on files
for (glf in seq_along(gl_sources)) {
### Zen Path
zen_backup_file <- paste0("https://zenodo.org/records/",
zen_id,
"/files/",
gl_sources[glf])
## Download the file
download.file(url = zen_backup_file,
destfile = gl_files[glf])
}
rm(gl_files)
} else { ## Local mode
ebaii_session <- '2538_eb3i_n1_2025'
localbackup_dir <- paste0('/shared/projects/', ebaii_session, '/atelier_scrnaseq/TD/RESOURCES/GENELISTS/')
mito_file <- paste0(localbackup_dir, '/mus_musculus_mito_symbols_20191015.rds')
ribo_source <- paste0(input_dir, '/mus_musculus_cribo_symbols_20191015.rds')
stress_source <- paste0(input_dir, '/mus_musculus_stress_symbols_20200224.rds')
}
Description:
This file describes the different steps to the quality control based on:
- the number of UMI (transcripts) detected per cell
- the number of genes detected per cell
- the proportion of transcripts related to the genes encoded in the mitochondria, per cell
- the proportion of transcripts related to the genes encoding ribosomal units, per cell
- the proportion of transcripts related to stress signature, per cell
Input data: Seurat object containing all cells (filtered count matrix)
Output data: Seurat object annotated for the cells to filter out for downstream analysis, called sobj_TD3A_qc_annotated.rds
Next steps: Cells annotation for cell type, doublet status and cell cycle status
Context
In this file, we used a dataset from the Paiva et al. publication.
The study concerns thymus autonomy:
The thymus is an “organ of passage”, critical in its function to the adaptive immune system for the maturation of T cell lymphocytes.
This maturation involves two main steps, performed thanks to macrophages:
- Positive selection : keeping cells that successfully develop react appropriately with MHC immune receptors of the body
- Negative selection : keeping cells that do not react against natural proteins of the body.
Thymus autonomy is a natural mechanism that allows to create T cells in the thymus by differentiation and cell competition, even when normal progenitors from the bone marrow are lacking, in critical conditions.
This mechanism is known in its effects, but the cells involved in are not.
This study is of importance in the health field, as this mechanism relies on a temporary loss of control of the cell normal functions.
The consequence is that if thymus is in autonomy for too long (few weeks), this is a prelude for leukemia !

Organism is : mus musculus
Individuals are : mice in development, grafted
The design corresponds to two conditions (Test / control)
- Control : thymus from wild type newborn mouse transplanted into wild type juvenile mouse. In this control case, donor T-cells progenitors (DN3) were replaced by host cells 3 weeks after transplantation.
- Test : thymus from wild type newborn mouse transplanted in KO Rag-/- type juvenile mouse (the KO partially impairs their ability to produce T-cell progenitors in normal amounts). In this test case, donor T-cells progenitors (DN3) were replaced by host cells 9 weeks after transplantation, showing that the donor DN3 cells outlived their normal lifespan by ~6 weeks.

You will mainly work on the KO sample (‘TD3A’). The input data consists in a count matrix, as a gzipped tabular text file, that contains everything needed to create a basic Seurat object :
* The expressions counts
* The feature names (here, gene symbols)
* The barcode names
This matrix has already been filtered for empty droplets.
Environment
We load the package of interest:
library(dplyr)
library(ggplot2)
library(Seurat)
.libPaths()
Show output
[1] "/shared/ifbstor1/home/smella/R/x86_64-conda-linux-gnu-library/4.2"
[2] "/shared/ifbstor1/software/miniconda/envs/r-4.4.1/lib/R/library"
Data
We load the count matrix:
# # Set input data
# input_data = "/shared/projects/2538_eb3i_n1_2025/atelier_scrnaseq/TD/BACKUP/TSV/GSM4861194_gex_2_raw_gene_expression.tsv.gz"
#
# # Load data
# mat = read.table(input_data,
# header = TRUE,
# sep = "\t")
# mat = as.matrix(mat)
# mat = Matrix::Matrix(mat,
# sparse = TRUE)
dim(scmat)
Show output
[1] 31053 4587
Show output
AAACCTGAGACGCTTT.1 AAACCTGAGGCATTGG.1 AAACCTGGTCAACATC.1
Xkr4 0 0 0
Gm1992 0 0 0
Gm37381 0 0 0
Rp1 0 0 0
Sox17 0 0 0
AAACCTGTCGAGGTAG.1 AAACCTGTCGATCCCT.1
Xkr4 0 0
Gm1992 0 0
Gm37381 0 0
Rp1 0 0
Sox17 0 0
We build a Seurat object from the count matrix:
sobj = Seurat::CreateSeuratObject(counts = scmat,
assay = "RNA",
project = "TD3A")
sobj
Show output
An object of class Seurat
31053 features across 4587 samples within 1 assay
Active assay: RNA (31053 features, 0 variable features)
1 layer present: counts
We do not need the count matrix:
We set the filtering thresholds based on quality control-related metrics. Adjust them as necessary based on the figures.
cut_nCount_RNA = 300
cut_log10_nCount_RNA <- 3
cut_nFeature_RNA = 750
cut_percent_mt = 5
cut_percent_rb = 30
cut_percent_st = 6
We define a nice palette to visualize the QC metrics:
color_palette = c("lightgray", "#FDBB84", "#EF6548", "#7F0000", "black")
For the QC metrics related to the proportion of UMI belongs to a specific gene sets, we need to load the gene sets.
mito_symbols = readRDS(mito_file)
mito_symbols
Show output
[1] "mt-Atp6" "mt-Atp8" "mt-Co1" "mt-Co2" "mt-Co3" "mt-Cytb" "mt-Nd1"
[8] "mt-Nd2" "mt-Nd3" "mt-Nd4" "mt-Nd4l" "mt-Nd5" "mt-Nd6" "mt-Rnr1"
[15] "mt-Rnr2" "mt-Ta" "mt-Tc" "mt-Td" "mt-Te" "mt-Tf" "mt-Tg"
[22] "mt-Th" "mt-Ti" "mt-Tk" "mt-Tl1" "mt-Tl2" "mt-Tm" "mt-Tn"
[29] "mt-Tp" "mt-Tq" "mt-Tr" "mt-Ts1" "mt-Ts2" "mt-Tt" "mt-Tv"
[36] "mt-Tw" "mt-Ty"
ribo_symbols = readRDS(ribo_file)
ribo_symbols
Show output
[1] "Rpsa" "Rps2" "Rps3" "Rps3a" "Rps4x" "Rps5" "Rps6" "Rps7"
[9] "Rps8" "Rps9" "Rps10" "Rps11" "Rps12" "Rps13" "Rps14" "Rps15"
[17] "Rps15a" "Rps16" "Rps17" "Rps18" "Rps19" "Rps20" "Rps21" "Rps23"
[25] "Rps24" "Rps25" "Rps26" "Rps27" "Rps27a" "Rps28" "Rps29" "Rps30"
[33] "Rpl3" "Rpl4" "Rpl5" "Rpl6" "Rpl7" "Rpl7a" "Rpl8" "Rpl9"
[41] "Rpl10" "Rpl10a" "Rpl11" "Rpl12" "Rpl13" "Rpl13a" "Rpl14" "Rpl15"
[49] "Rpl17" "Rpl18" "Rpl18a" "Rpl19" "Rpl21" "Rpl22" "Rpl23" "Rpl23a"
[57] "Rpl24" "Rpl26" "Rpl27" "Rpl27a" "Rpl28" "Rpl29" "Rpl30" "Rpl31"
[65] "Rpl32" "Rpl34" "Rpl35" "Rpl35a" "Rpl36" "Rpl44" "Rpl37" "Rpl37a"
[73] "Rpl38" "Rpl39" "Rpl40" "Rpl41" "Rplp0" "Rplp1" "Rplp2"
stress_symbols = readRDS(stress_file)
stress_symbols
Show output
[1] "Atf3" "Cmss1" "Diaph1" "Egr1" "Fos" "Gls"
[7] "Gm48099" "lsp90aa1" "lsp90ab1" "lfrd1" "Jak1" "Junb"
[13] "Kpna1" "Nup210l" "Peak1" "Stat3" "Actb" "Camk1d"
[19] "Chka" "Clic4" "Fodl2" "Hspa8" "Jun" "Jund"
[25] "Klf6" "Litaf" "Pecam1" "Ptma" "Sik3" "Spag9"
[31] "Arih1" "Azin1" "Brd2" "Chd4" "Ctnnb1" "Dennd4a"
[37] "Elf1" "G3bp1" "Ints6" "Kdm6b" "Lsmem1" "Man1a"
[43] "Med13" "Nfkbia" "Nfkbiz" "Nop58" "Piezo1" "Ppp1r15a"
[49] "Prkcg" "Sqstm1" "Taf4b" "Tmsb4x" "Ubc" "Zfp36"
[55] "Abtb2" "Adamts1" "Adamts9" "Ahnak" "Ankrd28" "Atp2a2"
[61] "Baz1a" "Btg2" "Ccdc138" "Cd44" "Cdkn1a" "Elf2"
[67] "Ep400" "Epas1" "Erf" "Ern1" "Fabp4" "Fosb"
[73] "Gm10073" "Gsk3a" "H3f3a" "H3f3b" "Hivep2" "Klf4"
[79] "Lmna" "lapkapk2" "Mapre1" "Msn" "Mylip" "Nabp1"
[85] "Ncl" "Nsd3" "Nufip2" "Plekhg2" "Ppp1cb" "Rnf19b"
[91] "Rps20" "Rtn4" "Runx1" "Sertad2" "Sptan1" "Top1"
[97] "Vcl" "Zbtb11"
We keep only the gene symbols available in our data:
mito_symbols = intersect(mito_symbols, rownames(sobj))
mito_symbols
Show output
[1] "mt-Atp6" "mt-Atp8" "mt-Co1" "mt-Co2" "mt-Co3" "mt-Cytb" "mt-Nd1"
[8] "mt-Nd2" "mt-Nd3" "mt-Nd4" "mt-Nd4l" "mt-Nd5" "mt-Nd6"
ribo_symbols = intersect(ribo_symbols, rownames(sobj))
ribo_symbols
Show output
[1] "Rpsa" "Rps2" "Rps3" "Rps4x" "Rps5" "Rps6" "Rps7" "Rps8"
[9] "Rps9" "Rps10" "Rps11" "Rps12" "Rps13" "Rps14" "Rps15" "Rps15a"
[17] "Rps16" "Rps17" "Rps18" "Rps19" "Rps20" "Rps21" "Rps23" "Rps24"
[25] "Rps25" "Rps26" "Rps27" "Rps27a" "Rps28" "Rps29" "Rpl3" "Rpl4"
[33] "Rpl5" "Rpl6" "Rpl7" "Rpl7a" "Rpl8" "Rpl9" "Rpl10" "Rpl10a"
[41] "Rpl11" "Rpl12" "Rpl13" "Rpl13a" "Rpl14" "Rpl15" "Rpl17" "Rpl18"
[49] "Rpl18a" "Rpl19" "Rpl21" "Rpl22" "Rpl23" "Rpl23a" "Rpl24" "Rpl26"
[57] "Rpl27" "Rpl27a" "Rpl28" "Rpl29" "Rpl30" "Rpl31" "Rpl32" "Rpl34"
[65] "Rpl35" "Rpl35a" "Rpl36" "Rpl37" "Rpl37a" "Rpl38" "Rpl39" "Rpl41"
[73] "Rplp0" "Rplp1" "Rplp2"
stress_symbols = intersect(stress_symbols, rownames(sobj))
stress_symbols
Show output
[1] "Atf3" "Cmss1" "Diaph1" "Egr1" "Fos" "Gls"
[7] "Gm48099" "Jak1" "Junb" "Kpna1" "Nup210l" "Peak1"
[13] "Stat3" "Actb" "Camk1d" "Chka" "Clic4" "Hspa8"
[19] "Jun" "Jund" "Klf6" "Litaf" "Pecam1" "Ptma"
[25] "Sik3" "Spag9" "Arih1" "Azin1" "Brd2" "Chd4"
[31] "Ctnnb1" "Dennd4a" "Elf1" "G3bp1" "Ints6" "Kdm6b"
[37] "Lsmem1" "Man1a" "Med13" "Nfkbia" "Nfkbiz" "Nop58"
[43] "Piezo1" "Ppp1r15a" "Prkcg" "Sqstm1" "Taf4b" "Tmsb4x"
[49] "Ubc" "Zfp36" "Abtb2" "Adamts1" "Adamts9" "Ahnak"
[55] "Ankrd28" "Atp2a2" "Baz1a" "Btg2" "Ccdc138" "Cd44"
[61] "Cdkn1a" "Elf2" "Ep400" "Epas1" "Erf" "Ern1"
[67] "Fabp4" "Fosb" "Gm10073" "Gsk3a" "H3f3a" "H3f3b"
[73] "Hivep2" "Klf4" "Lmna" "Mapre1" "Msn" "Mylip"
[79] "Nabp1" "Ncl" "Nsd3" "Nufip2" "Plekhg2" "Ppp1cb"
[85] "Rnf19b" "Rps20" "Rtn4" "Runx1" "Sertad2" "Sptan1"
[91] "Top1" "Vcl" "Zbtb11"
Note: A more robust analysis may use the gene identifiers instead of gene symbols.
Fast-processing
To visualize the low quality cells, we generate a projection. Understanding the commands below is the purpose of next course sessions. So just run:
sobj = Seurat::NormalizeData(sobj)
sobj = Seurat::ScaleData(sobj)
sobj = Seurat::FindVariableFeatures(sobj)
sobj = Seurat::RunPCA(sobj)
sobj = Seurat::RunUMAP(sobj, dims = c(1:20))
sobj
Show output
An object of class Seurat
31053 features across 4587 samples within 1 assay
Active assay: RNA (31053 features, 2000 variable features)
3 layers present: counts, data, scale.data
2 dimensional reductions calculated: pca, umap
We can now visualize the cells on a 2D projection:
Seurat::DimPlot(sobj,
reduction = "umap",
cols = "black")
Show plot

Quality control
Compute metrics
What is already available in the Seurat object ?
Show output
orig.ident nCount_RNA nFeature_RNA
AAACCTGAGACGCTTT.1 TD3A 2813 1594
AAACCTGAGGCATTGG.1 TD3A 2072 1365
AAACCTGGTCAACATC.1 TD3A 2025 1341
AAACCTGTCGAGGTAG.1 TD3A 1877 1241
AAACCTGTCGATCCCT.1 TD3A 2216 1441
AAACGGGCACTTACGA.1 TD3A 2445 1428
How do the two first QC metrics vary ?
Show output
orig.ident nCount_RNA nFeature_RNA
TD3A:4587 Min. : 502 Min. : 22
1st Qu.: 1986 1st Qu.:1291
Median : 2397 Median :1476
Mean : 3573 Mean :1638
3rd Qu.: 3134 3rd Qu.:1733
Max. :48875 Max. :5975
In the column nCount_RNA, the maximum is far from the third quartile. For visualization purpose, we transform this column to log10 scale.
sobj$log10_nCount_RNA = log10(sobj$nCount_RNA)
summary(sobj@meta.data)
Show output
orig.ident nCount_RNA nFeature_RNA log10_nCount_RNA
TD3A:4587 Min. : 502 Min. : 22 Min. :2.701
1st Qu.: 1986 1st Qu.:1291 1st Qu.:3.298
Median : 2397 Median :1476 Median :3.380
Mean : 3573 Mean :1638 Mean :3.429
3rd Qu.: 3134 3rd Qu.:1733 3rd Qu.:3.496
Max. :48875 Max. :5975 Max. :4.689
We compute the percentage of UMI related for each of the three list of genes. First, we compute the proportion of transcripts related to the genes encoded in the mitochondria, per cell.
sobj = Seurat::PercentageFeatureSet(
sobj,
assay = "RNA",
features = mito_symbols,
col.name = "percent_mt")
# # Alternative way to do (almost) the same:
# sobj = Seurat::PercentageFeatureSet(
# sobj,
# assay = "RNA",
# pattern = "^mt-",
# col.name = "percent_mt")
summary(sobj$percent_mt)
Show output
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.000 1.985 2.489 2.997 3.125 97.250
Then, we compute the proportion of transcripts related to the genes encoding ribosomal units, per cell:
sobj = Seurat::PercentageFeatureSet(
sobj,
assay = "RNA",
features = ribo_symbols,
col.name = "percent_rb")
# # Alternative way to do (almost) the same:
# sobj = Seurat::PercentageFeatureSet(
# sobj,
# assay = "RNA",
# pattern = "^Rp[l|s][a-z]?[0-9]*[a,x]?$",
# col.name = "percent_rb")
summary(sobj$percent_rb)
Show output
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.000 7.884 9.577 10.880 12.167 42.010
Finally, we compute the proportion of transcripts related to stress signature, per cell:
sobj = Seurat::PercentageFeatureSet(
sobj,
assay = "RNA",
features = stress_symbols,
col.name = "percent_st")
summary(sobj$percent_st)
Show output
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.000 2.946 3.338 3.368 3.749 10.198
We know have all the QC-related metrics:
Show output
orig.ident nCount_RNA nFeature_RNA log10_nCount_RNA percent_mt
TD3A:4587 Min. : 502 Min. : 22 Min. :2.701 Min. : 0.000
1st Qu.: 1986 1st Qu.:1291 1st Qu.:3.298 1st Qu.: 1.985
Median : 2397 Median :1476 Median :3.380 Median : 2.489
Mean : 3573 Mean :1638 Mean :3.429 Mean : 2.997
3rd Qu.: 3134 3rd Qu.:1733 3rd Qu.:3.496 3rd Qu.: 3.125
Max. :48875 Max. :5975 Max. :4.689 Max. :97.250
percent_rb percent_st
Min. : 0.000 Min. : 0.000
1st Qu.: 7.884 1st Qu.: 2.946
Median : 9.577 Median : 3.338
Mean :10.880 Mean : 3.368
3rd Qu.:12.167 3rd Qu.: 3.749
Max. :42.010 Max. :10.198
Failing cells
We identify the cells that do not pass the quality control. This will be used for the visualization and for filtering. If the filtering thresholds are modified, do not forget to run again this chunk.
fail_percent_mt = sobj@meta.data %>%
dplyr::filter(percent_mt > cut_percent_mt) %>%
rownames()
fail_percent_rb = sobj@meta.data %>%
dplyr::filter(percent_rb > cut_percent_rb) %>%
rownames()
fail_percent_st = sobj@meta.data %>%
dplyr::filter(percent_st > cut_percent_st) %>%
rownames()
fail_nCount_RNA = sobj@meta.data %>%
dplyr::filter(nCount_RNA < cut_nCount_RNA) %>%
rownames()
fail_nFeature_RNA = sobj@meta.data %>%
dplyr::filter(nFeature_RNA < cut_nFeature_RNA) %>%
rownames()
Visualization
This is difficult to handle the distribution of these metrics across cells. We opt for various visualization ways:
- histogram, showing the distribution of the metric
- violin/box plot, showing the distribution of the metric, useful if several datasets are considered
- UMAP (or alternatively, tSNE), showing the distribution of the metric over a 2D projection of cells
You may choose one of these visualization ways.
Number of UMI
Define the code
We write a lot of code to create our figures of interest:
# Histogram showing:
# - the distribution of the metric
# - an estimated density
# - the filtering threshold as a straight line
p_hist = ggplot(sobj@meta.data, aes(x = log10_nCount_RNA)) +
geom_histogram(aes(y = after_stat(density)),
colour = "black", fill = "#F8766D", bins = 100) +
geom_density(alpha = 0, col = "blue", lwd = 0.75) +
geom_vline(xintercept = cut_log10_nCount_RNA, col = "red") +
labs(title = paste0("Threshold for log10_nCount_RNA is: ", cut_log10_nCount_RNA)) +
theme_classic() +
theme(plot.title = element_text(hjust = 0.5))
# Violin plot:
# - is easily accessible in the Seurat package
# - can or not display the cells (set `pt.size = 0` to hide the cells)
# - is useful when several datasets have been merged
p_violin = Seurat::VlnPlot(sobj, features = "log10_nCount_RNA") +
geom_hline(yintercept = cut_log10_nCount_RNA, col = "red") +
theme(axis.title.x = element_blank(),
legend.position = "none")
# # Box plot is similar to violin plot but not in the Seurat package
# p_boxplot = ggplot(sobj@meta.data, aes(y = log10_nCount_RNA,
# x = orig.ident)) +
# geom_boxplot(colour = "black", fill = "#F8766D") +
# geom_jitter(width = 0.3, size = 0.001) +
# geom_hline(yintercept = cut_log10_nCount_RNA, col = "red") +
# theme_classic() +
# theme(axis.title.x = element_blank())
# This 2D representation shows the distribution of the metric over cells
p_umap = FeaturePlot(sobj,
reduction = "umap",
features = "log10_nCount_RNA") +
scale_color_gradientn(colors = color_palette) +
theme(aspect.ratio = 1)
# This 2D representation shows the low quality cells in color
# `order = "fail"` is used to display failing cells on front
# before, we need to define the "failorpass" column in sobj@meta.data
# It corresponds to "fail" for cells that do no pass the threshold,
# or "pass" otherwise
sobj$failorpass = ifelse(colnames(sobj) %in% fail_nCount_RNA,
yes = "fail", no = "pass") %>%
as.factor()
p_fail = DimPlot(sobj,
group.by = "failorpass",
order = "fail") +
scale_color_manual(values = c("#F8766D", "gray80"),
breaks = levels(sobj$failorpass)) +
labs(title = "log10_nCount_RNA",
subtitle = paste0(length(fail_nCount_RNA), " cells fail (",
round(100*length(fail_nCount_RNA)/ncol(sobj), 2), " %)")) +
theme(aspect.ratio = 1,
plot.title = element_text(hjust = 0.5),
plot.subtitle = element_text(hjust = 0.5))
sobj$failorpass = NULL # remove the column (it was temporary)
# We use the patchwork package to arrange all figures together
patchwork::wrap_plots(p_umap, p_fail, p_hist, p_violin) +
patchwork::plot_layout(nrow = 1, widths = c(1, 1, 2, 1))
Show plot

Define a function
Instead of copying-pasting this section of code for all QC-metrics, we design a function using the template below:
my_function_name = function(param1, param2) {
# do something with the parameter values
output = "something"
return(output)
}
Here is the function:
print_1_qc_metric = function(object = sobj,
qc = "log10_nCount_RNA",
cut_qc = cut_log10_nCount_RNA,
failing_cells = fail_nCount_RNA) {
# Description of the parameters:
# - sobj : the Seurat object, with default value to the one
# - qc : CHARACTER : the QC metric, must be a column in sobj@meta.data
# - cut_qc : NUMERIC : the filtering threshold for the QC metric
# - failing_cells : CHARACTER VECTOR : the cells that fail the QC
# Histogram
p_hist = ggplot(object@meta.data, aes(x = .data[[qc]])) +
geom_histogram(aes(y = after_stat(density)),
colour = "black", fill = "#F8766D", bins = 100) +
geom_density(alpha = 0, col = "blue", lwd = 0.75) +
geom_vline(xintercept = cut_qc, col = "red") +
labs(title = paste0("Threshold for ", qc, " is: ", cut_qc)) +
theme_classic() +
theme(plot.title = element_text(hjust = 0.5))
# Violin plot
p_violin = Seurat::VlnPlot(object, features = qc) +
geom_hline(yintercept = cut_qc, col = "red") +
theme(axis.title.x = element_blank(),
legend.position = "none")
# Box plot
p_boxplot = ggplot(object@meta.data, aes(y = .data[[qc]],
x = "orig.ident")) +
geom_boxplot(colour = "black", fill = "#F8766D") +
geom_jitter(width = 0.3, size = 0.001) +
geom_hline(yintercept = cut_qc, col = "red") +
theme_classic() +
theme(axis.title.x = element_blank())
# Feature plot
p_umap = Seurat::FeaturePlot(object,
reduction = "umap",
features = qc) +
scale_color_gradientn(colors = color_palette) +
theme(aspect.ratio = 1)
# Dim plot
object$failorpass = ifelse(colnames(object) %in% failing_cells,
yes = "fail", no = "pass") %>%
as.factor()
p_fail = Seurat::DimPlot(object,
group.by = "failorpass",
order = "fail") +
scale_color_manual(values = c("#F8766D", "gray80"),
breaks = levels(object$failorpass)) +
labs(title = qc,
subtitle = paste0(length(failing_cells), " cells fail (",
round(100*length(failing_cells)/ncol(sobj), 2), " %)")) +
theme(aspect.ratio = 1,
plot.title = element_text(hjust = 0.5),
plot.subtitle = element_text(hjust = 0.5))
# Patchwork
p = patchwork::wrap_plots(p_umap, p_fail, p_hist, p_violin, p_boxplot) +
patchwork::plot_layout(nrow = 1, widths = c(1, 1, 2, 1, 1))
return(p)
}
Check the function
This is working for log10_nCount_RNA, because it is used to define default parameter values:
Show plot

but also works by specifying the parameter values:
print_1_qc_metric(sobj,
qc = "log10_nCount_RNA",
cut_qc = cut_log10_nCount_RNA,
failing_cells = fail_nCount_RNA)
Show plot

So we can copy-paste only this chunk for the next QC metrics !
Number of genes
print_1_qc_metric(sobj,
qc = "nFeature_RNA",
cut_qc = cut_nFeature_RNA,
failing_cells = fail_nFeature_RNA)
Show plot

Mitochondrial genes expression
print_1_qc_metric(sobj,
qc = "percent_mt",
cut_qc = cut_percent_mt,
failing_cells = fail_percent_mt)
Show plot

Ribosomal genes expression
print_1_qc_metric(sobj,
qc = "percent_rb",
cut_qc = cut_percent_rb,
failing_cells = fail_percent_rb)
Show plot

Filtering
We could filter out cells based on these 5 QC metrics now. It is also possible to wait, perform various annotations such as cell type annotation or cell cycle phase scoring, to better characterize the low quality cells.
To filter a Seurat object, we use the subset function:
sobj_filtered = subset(sobj, invert = TRUE,
cells = unique(c(fail_nCount_RNA, fail_nFeature_RNA,
fail_percent_mt, fail_percent_rb, fail_percent_st)))
sobj_filtered
Show output
An object of class Seurat
31053 features across 4218 samples within 1 assay
Active assay: RNA (31053 features, 2000 variable features)
3 layers present: counts, data, scale.data
2 dimensional reductions calculated: pca, umap
We are going to save the object annotated for cells that fail the quality control, regardless the metrics. So we add a single column to sobj@meta.data :
sobj$fail_qc = ifelse(test = colnames(sobj) %in% colnames(sobj_filtered),
yes = "pass",
no = "fail")
table(sobj$fail_qc)
Show output
fail pass
369 4218
Save
We save the non-filtered Seurat object:
saveRDS(sobj, file = paste0(output_dir, "/sobj_TD3A_qc_annotated.rds"))
This Seurat object can then be the input object for annotation, definition of a new projection and downstream analyses.
R session
This is a good practice to show the version of the packages used in this notebook.
Show output
R version 4.4.1 (2024-06-14)
Platform: x86_64-conda-linux-gnu
Running under: Ubuntu 22.04.5 LTS
Matrix products: default
BLAS/LAPACK: /shared/ifbstor1/software/miniconda/envs/r-4.4.1/lib/libopenblasp-r0.3.29.so; LAPACK version 3.12.0
locale:
[1] LC_CTYPE=en_US.UTF-8 LC_NUMERIC=C
[3] LC_TIME=en_US.UTF-8 LC_COLLATE=en_US.UTF-8
[5] LC_MONETARY=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8
[7] LC_PAPER=en_US.UTF-8 LC_NAME=C
[9] LC_ADDRESS=C LC_TELEPHONE=C
[11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C
time zone: Europe/Paris
tzcode source: system (glibc)
attached base packages:
[1] stats graphics grDevices utils datasets methods base
other attached packages:
[1] Seurat_5.3.0 SeuratObject_5.1.0 sp_2.2-0 ggplot2_3.5.2
[5] dplyr_1.1.4
loaded via a namespace (and not attached):
[1] deldir_2.0-4 pbapply_1.7-2 gridExtra_2.3
[4] rlang_1.1.6 magrittr_2.0.3 RcppAnnoy_0.0.22
[7] spatstat.geom_3.4-1 matrixStats_1.5.0 ggridges_0.5.6
[10] compiler_4.4.1 png_0.1-8 vctrs_0.6.5
[13] reshape2_1.4.4 stringr_1.5.1 pkgconfig_2.0.3
[16] fastmap_1.2.0 labeling_0.4.3 rmdformats_1.0.4
[19] promises_1.3.2 rmarkdown_2.29 ggbeeswarm_0.7.2
[22] purrr_1.0.4 xfun_0.52 cachem_1.1.0
[25] jsonlite_2.0.0 goftest_1.2-3 later_1.4.2
[28] spatstat.utils_3.1-4 irlba_2.3.5.1 parallel_4.4.1
[31] cluster_2.1.6 R6_2.6.1 ica_1.0-3
[34] spatstat.data_3.1-6 bslib_0.9.0 stringi_1.8.7
[37] RColorBrewer_1.1-3 reticulate_1.42.0 spatstat.univar_3.1-3
[40] parallelly_1.45.0 lmtest_0.9-40 jquerylib_0.1.4
[43] scattermore_1.2 Rcpp_1.0.14 bookdown_0.39
[46] knitr_1.50 tensor_1.5 future.apply_1.11.3
[49] zoo_1.8-14 sctransform_0.4.2 httpuv_1.6.15
[52] Matrix_1.7-3 splines_4.4.1 igraph_2.1.4
[55] tidyselect_1.2.1 abind_1.4-8 rstudioapi_0.17.1
[58] dichromat_2.0-0.1 yaml_2.3.10 spatstat.random_3.4-1
[61] codetools_0.2-20 miniUI_0.1.2 spatstat.explore_3.4-3
[64] listenv_0.9.1 lattice_0.22-6 tibble_3.2.1
[67] plyr_1.8.9 shiny_1.10.0 withr_3.0.2
[70] ROCR_1.0-11 ggrastr_1.0.2 evaluate_1.0.3
[73] Rtsne_0.17 future_1.49.0 fastDummies_1.7.5
[76] survival_3.7-0 polyclip_1.10-7 fitdistrplus_1.2-2
[79] pillar_1.10.2 KernSmooth_2.23-24 plotly_4.10.4
[82] generics_0.1.4 RcppHNSW_0.6.0 scales_1.4.0
[85] globals_0.18.0 xtable_1.8-4 glue_1.8.0
[88] lazyeval_0.2.2 tools_4.4.1 data.table_1.17.4
[91] RSpectra_0.16-2 RANN_2.6.2 dotCall64_1.2
[94] cowplot_1.1.3 grid_4.4.1 tidyr_1.3.1
[97] nlme_3.1-165 patchwork_1.3.0 beeswarm_0.4.0
[100] vipor_0.4.7 cli_3.6.5 spatstat.sparse_3.1-0
[103] spam_2.11-1 viridisLite_0.4.2 uwot_0.2.3
[106] gtable_0.3.6 sass_0.4.10 digest_0.6.37
[109] progressr_0.15.1 ggrepel_0.9.6 htmlwidgets_1.6.4
[112] farver_2.1.2 htmltools_0.5.8.1 lifecycle_1.0.4
[115] httr_1.4.7 mime_0.13 MASS_7.3-65
LS0tCnRpdGxlOiAiPENFTlRFUj5FQjNJIG4xIDIwMjUgc2NSTkFzZXE8QlI+LTxCUj4gPEI+TEVTU09OIEJMT0NLIChJKTwvQj48QlI+LTxCUj5BIG5pY2Ugc3VidGl0bGU8L0NFTlRFUj4iCmRhdGU6ICIyMDI1LTE2LTIxLjIyIgphdXRob3I6CiAgLSBuYW1lOiAiRUJBSUkgbjEgc2NSTkFzZXEgVGVhbSIKICAtIG5hbWU6ICJGaXJzdCBBVVRIT1IiCiAgICBlbWFpbDogImZpcnN0LmF1dGhvckBzY3JuYXNlcS5jb20iCiAgLSBuYW1lOiAiU2Vjb25kIEFVVEhPUiIKICAgIGVtYWlsOiAic2Vjb25kLmF1dGhvckBzY3JuYXNlcS5jb20iCm91dHB1dDoKICBybWRmb3JtYXRzOjpyZWFkdGhlZG93bjoKICAgIGZpZ193aWR0aDogOAogICAgZmlnX2hlaWdodDogNgogICAgaGlnaGxpZ2h0OiB0YW5nbyAgIyMgVGhlbWUgZm9yIHRoZSBjb2RlIGNodW5rcwogICAgZW1iZWRfZm9udHM6IFRSVUUKICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZSAgIyMgQWRkcyBudW1iZXIgdG8gaGVhZGVycyAoc2VjdGlvbnMpCiAgICB0aGVtZTogZmxhdGx5ICAjIyBDU1MgdGhlbWUgZm9yIHRoZSBIVE1MIHBhZ2UKICAgIGNvbGxhcHNlZDogdHJ1ZSAgIyMgQnkgZGVmYXVsdCwgdGhlIFRPQyBpcyBmb2xkZWQKICAgIHRvY19kZXB0aDogMwogICAgc21vb3RoX3Njcm9sbDogdHJ1ZSAjIyBTbW9vdGggc2Nyb2xsIG9mIHRoZSBIVE1MIHBhZ2UKICAgIHNlbGZfY29udGFpbmVkOiB0cnVlICMjIEluY2x1ZGVzIGFsbCBwbG90cy9pbWFnZXMgd2l0aGluIHRoZSBIVE1MCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlICMjIEFkZHMgYSBidXR0b24gdG8gZG93bmxvYWQgdGhlIFJtZAogICAgY29kZV9mb2xkaW5nOiBzaG93CiAgICB0aHVtYm5haWxzOiBmYWxzZQogICAgbGlnaHRib3g6IHRydWUKICAgIGZpZ19jYXB0aW9uOiBmYWxzZQogICAgZ2FsbGVyeTogdHJ1ZQogICAgdXNlX2Jvb2tkb3duOiB0cnVlCmFsd2F5c19hbGxvd19odG1sOiB0cnVlICMjIEFsbG93IHBsYWluIEhUTUwgY29kZSBpbiB0aGUgUm1kCmVkaXRvcl9vcHRpb25zOiAKICBtYXJrZG93bjogCiAgICB3cmFwOiA3MgotLS0KCjwhLS0ga25pdCBzZXR1cCAtLT4KCmBgYHtyIGtuaXRfc2V0dXAsIGVjaG8gPSBGQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KAogIGVjaG8gPSBUUlVFLCAgICAgICAgIyBQcmludCB0aGUgY29kZQogIGV2YWwgPSBUUlVFLCAgICAgICAgIyBSdW4gY29tbWFuZCBsaW5lcwogIG1lc3NhZ2UgPSBGQUxTRSwgICAgIyBQcmludCBtZXNzYWdlcwogIHByb21wdCA9IEZBTFNFLCAgICAgIyBEbyBub3QgZGlzcGxheSBwcm9tcHQKICBjb21tZW50ID0gTkEsICAgICAgICMgTm8gY29tbWVudHMgb24gdGhpcyBzZWN0aW9uCiAgd2FybmluZyA9IEZBTFNFLCAgICAjIERpc3BsYXkgd2FybmluZ3MKICB0aWR5ID0gRkFMU0UsCiAgZmlnLmFsaWduPSJjZW50ZXIiLCAKICAjIHJlc3VsdHMgPSAnaGlkZScsCiAgd2lkdGggPSAxMDAgICAgICAgIyBOdW1iZXIgb2YgY2hhcmFjdGVycyBwZXIgbGluZQopCmBgYAoKCjwhLS0gQ1NTIHRvIGNvbG9yIGNodW5rcyBhbmQgb3V0cHV0cyAtLT4KCgpgYGB7Y3NzLCBlY2hvPUZBTFNFfQoubm90cnVuIHsKICBiYWNrZ3JvdW5kLWNvbG9yOiBsaWdodGdyZXkgIWltcG9ydGFudDsKICBib3JkZXI6IDNweCBzb2xpZCBibGFjayAhaW1wb3J0YW50Owp9Ci5ub3RydW5vIHsKICBiYWNrZ3JvdW5kLWNvbG9yOiBsaWdodGdyZXkgIWltcG9ydGFudDsKICBjb2xvciA6IGJsYWNrICFpbXBvcnRhbnQ7Cn0KLnF1ZXN0aW9uIHsKICBiYWNrZ3JvdW5kLWNvbG9yOiBhcXVhbWFyaW5lICFpbXBvcnRhbnQ7CiAgY29sb3IgOiBibGFjayAhaW1wb3J0YW50OwogIGJvcmRlcjogM3B4IHNvbGlkIGxpbWVncmVlbiAhaW1wb3J0YW50Owp9Ci5xdWVzdGlvbm8gewogIGJhY2tncm91bmQtY29sb3I6IGFxdWFtYXJpbmUgIWltcG9ydGFudDsKICBjb2xvciA6IGJsYWNrICFpbXBvcnRhbnQ7Cn0KLmFuc3dlciB7CiAgYmFja2dyb3VuZC1jb2xvcjogbmF2YWpvd2hpdGUgIWltcG9ydGFudDsKICBib3JkZXI6IDNweCBzb2xpZCBicm93biAhaW1wb3J0YW50Owp9Ci5hbnN3ZXJvIHsKICBiYWNrZ3JvdW5kLWNvbG9yOiBuYXZham93aGl0ZSAhaW1wb3J0YW50OwogIGNvbG9yIDogYmxhY2sgIWltcG9ydGFudDsKfQouYmV5b25kIHsKICBiYWNrZ3JvdW5kLWNvbG9yOiB2aW9sZXQgIWltcG9ydGFudDsKICBib3JkZXI6IDNweCBzb2xpZCBwdXJwbGUgIWltcG9ydGFudDsKfQouYmV5b25kbyB7CiAgYmFja2dyb3VuZC1jb2xvcjogdmlvbGV0ICFpbXBvcnRhbnQ7CiAgY29sb3IgOiBibGFjayAhaW1wb3J0YW50Owp9CmBgYAoKCjwhLS0gSG9vayB0byBoYW5kbGUgY29kZSBibG9ja3Mgb3V0cHV0IGZvbGRpbmcgLS0+CgpgYGB7ciBrbml0X2hvb2ssIGVjaG8gPSBGQUxTRX0KaG9va3MgPSBrbml0cjo6a25pdF9ob29rcyRnZXQoKQpob29rX2ZvbGRhYmxlID0gZnVuY3Rpb24odHlwZSkgewogIGZvcmNlKHR5cGUpCiAgZnVuY3Rpb24oeCwgb3B0aW9ucykgewogICAgcmVzID0gaG9va3NbW3R5cGVdXSh4LCBvcHRpb25zKQogICAgCiAgICBpZiAoaXNGQUxTRShvcHRpb25zW1twYXN0ZTAoImZvbGQuIiwgdHlwZSldXSkpIHJldHVybihyZXMpCiAgICAKICAgIHBhc3RlMCgKICAgICAgIjxkZXRhaWxzPjxzdW1tYXJ5PlNob3cgIiwgdHlwZSwgIjwvc3VtbWFyeT5cblxuIiwKICAgICAgcmVzLAogICAgICAiXG5cbjwvZGV0YWlscz4iCiAgICApCiAgfQp9CmtuaXRyOjprbml0X2hvb2tzJHNldCgKICBvdXRwdXQgPSBob29rX2ZvbGRhYmxlKCJvdXRwdXQiKSwKICBwbG90ID0gaG9va19mb2xkYWJsZSgicGxvdCIpCikKYGBgCgoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCjxjZW50ZXI+IVtdKGViM2lfYmFubmVyLnBuZyk8L2NlbnRlcj4KCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgoKIyBTdGFydCBSc3R1ZGlvCgotICAgVXNpbmcgdGhlIFtPcGVuT25EZW1hbmQgY2hlYXQKICAgIHNoZWV0XShodHRwczovL2lmYi1lbGl4aXJmci5naXRodWIuaW8vRUJBSUkvMjAyMy9lYmFpaW4xL1NpbmdsZUNlbGwvMjAyNF9URF9PcGVuT25EZW1hbmQuaHRtbCl7dGFyZ2V0PSJfYmxhbmsifSAqKltMSU5LX1RPX1VQREFURV0qKiwKICAgIGNvbm5lY3QgdG8gdGhlIFtPcGVuT25EZW1hbmQKICAgIHBvcnRhbF0oaHR0cHM6Ly9vbmRlbWFuZC5jbHVzdGVyLmZyYW5jZS1iaW9pbmZvcm1hdGlxdWUuZnIpe3RhcmdldD0iX2JsYW5rIn0gYW5kCiAgICAqKmNyZWF0ZSBhIFJzdHVkaW8gc2Vzc2lvbioqIHdpdGggdGhlIHJpZ2h0IHJlc291cmNlIHJlcXVpcmVtZW50cywgdGhhbmtzIHRvIHRoZSBjaGVhdCBzaGVldC4KCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIFdhcm0tdXAKCi0gICBXZSBub3cgc2V0ICoqY29tbW9uIHBhcmFtZXRlcnMqKiBhcyBuZXcgdmFyaWFibGVzLCBvbmNlIGFuZCBmb3IgYWxsIGZvciB0aGlzCnNlc3Npb24gOgoKYGBge3Igc2V0cGFyYW19CiMgc2V0cGFyYW0KCgojIyBTZXQgeW91ciBwcm9qZWN0IG5hbWUKIyBXQVJOSU5HIDogRG8gbm90IGp1c3QgY29weS1wYXN0ZSB0aGlzICEgSXQncyBNWSBwcm9qZWN0IG5hbWUgISBQdXQgWU9VUlMgISEKcHJvamVjdF9uYW1lIDwtICJlYmFpaV9zY190ZWFjaGVycyIKCgojIyBDb250cm9sIGlmIHRoZSBwcm9qZWN0X25hbWUgZXhpc3RzIG9uIHRoZSBjbHVzdGVyCmNhdCgnUEFUSCBDSEVDSyA6ICcsIGRpci5leGlzdHMocGFzdGUwKCcvc2hhcmVkL3Byb2plY3RzLycsIHByb2plY3RfbmFtZSkpKQoKIyMgU2VlZCBmb3IgdGhlIFJORwpteV9zZWVkIDwtIDEzMzdMCgojICMjIEVtcHR5IGRyb3BsZXRzIG1heCBwLXZhbHVlCiMgbWF4X3AgPC0gMUUtMDMKCmBgYAoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMgUHJlcGFyZSB0aGUgZGF0YSBzdHJ1Y3R1cmUgW1BSRVBST0MuMl0KCiMjIE1haW4gZGlyZWN0b3J5CgpgYGB7ciBtYWluZGlyfQojIG1haW5kaXIKCiMjIFByZXBhcmluZyB0aGUgcGF0aApURF9kaXIgPC0gcGFzdGUwKCIvc2hhcmVkL3Byb2plY3RzLyIsIHByb2plY3RfbmFtZSwgIi9TQ19URCIpCgojIyBDcmVhdGluZyB0aGUgcm9vdCBkaXJlY3RvcnkKIyBkaXIuY3JlYXRlKHBhdGggPSBURF9kaXIsIHJlY3Vyc2l2ZSA9IFRSVUUpCgojIyBQcmludCB0aGUgcm9vdCBkaXJlY3Rvcnkgb24tc2NyZWVuCnByaW50KFREX2RpcikKCmBgYAoKIyMgQ3VycmVudCBzZXNzaW9uCgpgYGB7ciBzZXNzaW9uZGlyfQojIHNlc3Npb25kaXIKCiMjIENyZWF0aW5nIHRoZSBzZXNzaW9uIChQcmVwcm9jLjIpIGRpcmVjdG9yeQpzZXNzaW9uX2RpciA8LSBwYXN0ZTAoVERfZGlyLCAiLzAyX1ByZXByb2MuMiIpCiMgZGlyLmNyZWF0ZShwYXRoID0gc2Vzc2lvbl9kaXIsIHJlY3Vyc2l2ZSA9IFRSVUUpCgojIyBQcmludCB0aGUgc2Vzc2lvbiBkaXJlY3Rvcnkgb24tc2NyZWVuCnByaW50KHNlc3Npb25fZGlyKQoKYGBgCgojIyBJbnB1dCBkaXJlY3RvcnkKCmBgYHtyIGluZGlyfQojIGluZGlyCgojIyBDcmVhdGluZyB0aGUgSU5QVVQgZGF0YSBkaXJlY3RvcnkKaW5wdXRfZGlyIDwtIHBhc3RlMChzZXNzaW9uX2RpciwgIi9EQVRBIikKIyBkaXIuY3JlYXRlKHBhdGggPSBpbnB1dF9kaXIsIHJlY3Vyc2l2ZSA9IFRSVUUpCgojIyBQcmludCB0aGUgaW5wdXQgZGlyZWN0b3J5IG9uLXNjcmVlbgpwcmludChpbnB1dF9kaXIpCgpgYGAKCiMjIEdlbmVsaXN0cyBkaXJlY3RvcnkKClRoaXMgaXMgYSBkaXJlY3Rvcnkgd2hlcmUgd2Ugd2lsbCBzdG9yZSBhZGRpdGlvbmFsIGluZm9ybWF0aW9uIGZyb20gCmtub3dsZWRnZSBiYXNlcyBhYm91dCBnZW5lcyB1c2VkIHRvIGVzdGltYXRlIHRoZSBjZWxsIGN5Y2xlIHBoYXNlIG9mIGNlbGxzLgoKYGBge3IgcmVzZGlyfQojIHJlc2RpcgoKcmVzX2RpciA8LSBwYXN0ZTAoVERfZGlyLCAiL1Jlc291cmNlcyIpCmdsaXN0X2RpciA8LSBwYXN0ZTAocmVzX2RpciwgIi9HZW5lbGlzdHMiKQoKIyMgQ3JlYXRlIHRoZSBkaXJlY3RvcnkKIyBkaXIuY3JlYXRlKHBhdGggPSBnbGlzdF9kaXIsIHJlY3Vyc2l2ZSA9IFRSVUUpCgojIyBQcmludCB0aGUgcmVzb3VyY2VzIGRpcmVjdG9yeSBvbi1zY3JlZW4KcHJpbnQoZ2xpc3RfZGlyKQoKYGBgCgojIyBPdXRwdXQgZGlyZWN0b3J5CgpgYGB7ciBvdXRkaXJ9CiMgb3V0ZGlyCgojIyBDcmVhdGluZyB0aGUgT1VUUFVUIGRhdGEgZGlyZWN0b3J5Cm91dHB1dF9kaXIgPC0gcGFzdGUwKHNlc3Npb25fZGlyLCAiL1JFU1VMVFMiKQojIGRpci5jcmVhdGUocGF0aCA9IG91dHB1dF9kaXIsIHJlY3Vyc2l2ZSA9IFRSVUUpCgojIyBQcmludCB0aGUgb3V0cHV0IGRpcmVjdG9yeSBvbi1zY3JlZW4KcHJpbnQob3V0cHV0X2RpcikKCmBgYAoKCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIExvYWQgdGhlIHJhdyBtYXRyaXggW1ByZVByb2MuMl0KCldlIHJldHJpZXZlIHRoZSBpbnB1dCBkYXRhIGZpbGUKCmBgYHtyIG1hdF9kbH0KIyBtYXRfZGwKCmxvY2FsIDwtIEZBTFNFCgojIyBUaGUgcmF3IGNvdW50IG1hdHJpeCB3ZSB3aWxsIHN0YXJ0IGZyb20Kc2NtYXRfc291cmNlIDwtICJHU000ODYxMTk0X2dleF8yX3Jhd19nZW5lX2V4cHJlc3Npb24udHN2Lmd6IgoKIyMgRG93bmxvYWQgdGhlIGZpbGUgZnJvbSBaZW5vZG8KaWYgKCFsb2NhbCkgewogIAogICMjIyBaZW5JRAogIHplbl9pZCA8LSAiMTQwMzM5NDEiCiAgIyMjIFplbiBQYXRoCiAgemVuX2JhY2t1cF9maWxlIDwtIHBhc3RlMCgiaHR0cHM6Ly96ZW5vZG8ub3JnL3JlY29yZHMvIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHplbl9pZCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICIvZmlsZXMvIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNjbWF0X3NvdXJjZSkKICAKICAjIyBUaGUgcGF0aCB0byB0aGUgbG9jYWxseSBzYXZlZCBpbnB1dCBmaWxlCiAgc2NtYXRfZmlsZSA8LSBwYXN0ZTAoaW5wdXRfZGlyLAogICAgICAgICAgICAgICAgICAgICAgICcvJywKICAgICAgICAgICAgICAgICAgICAgICBzY21hdF9zb3VyY2UpCiAgIyMgRG93bmxvYWQgdGhlIGZpbGUKICBkb3dubG9hZC5maWxlKHVybCA9IHplbl9iYWNrdXBfZmlsZSwKICAgICAgICAgICAgICAgIGRlc3RmaWxlID0gc2NtYXRfZmlsZSkKfSBlbHNlIHsKICBlYmFpaV9zZXNzaW9uIDwtICcyNTM4X2ViM2lfbjFfMjAyNScKICBzY21hdF9maWxlIDwtIHBhc3RlMCgKICAgICAgJy9zaGFyZWQvcHJvamVjdHMvJywKICAgICAgZWJhaWlfc2Vzc2lvbiwKICAgICAgJy9hdGVsaWVyX3Njcm5hc2VxL1REL0JBQ0tVUC9UU1YvJywKICAgICAgc2NtYXRfc291cmNlKQp9CmBgYAoKV2UgY2FuIGxvYWQgaXQgaW50byBSIDoKCmBgYHtyIG1hdF9sb2FkfQojIG1hdF9sb2FkCgpzY21hdCA8LSBhcy5tYXRyaXgoCiAgdXRpbHM6OnJlYWQudGFibGUoCiAgICBmaWxlID0gc2NtYXRfZmlsZSwgCiAgICBoZWFkZXIgPSBUUlVFLCAKICAgIHNlcCA9ICJcdCIpKQoKIyMgRGlzcGxheWluZyBpdHMgc2l6ZSBpbi1tZW1vcnkgKHRoaXMgaXMgYSBiYXNpYyBtYXRyaXgpCmZvcm1hdCh1dGlsczo6b2JqZWN0LnNpemUoc2NtYXQpLCB1bml0cyA9ICJhdXRvIikKCmBgYAoKCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIExvYWQgdGhlIGdlbmVsaXN0cyByZXNvdXJjZXMgW1ByZVByb2MuMl0KCldlIHJldHJpZXZlIHRoZSBnZW5lbGlzdHMKCmBgYHtyIGdsX2RsfQojIGdsX2RsCgpsb2NhbCA8LSBGQUxTRQoKIyMgVGhlIGdlbmVsaXN0IGZpbGVzCm1pdG9fc291cmNlIDwtICJtdXNfbXVzY3VsdXNfbWl0b19zeW1ib2xzXzIwMTkxMDE1LnJkcyIKcmlib19zb3VyY2UgPC0gIm11c19tdXNjdWx1c19jcmlib19zeW1ib2xzXzIwMTkxMDE1LnJkcyIKc3RyZXNzX3NvdXJjZSA8LSAibXVzX211c2N1bHVzX3N0cmVzc19zeW1ib2xzXzIwMjAwMjI0LnJkcyIKZ2xfc291cmNlcyA8LSBjKG1pdG9fc291cmNlLCByaWJvX3NvdXJjZSwgc3RyZXNzX3NvdXJjZSkKCiMjIFRoZSAoZnV0dXJlKSBsb2NhbCBmaWxlcwptaXRvX2ZpbGUgPC0gcGFzdGUwKGlucHV0X2RpciwgJy8nLCBtaXRvX3NvdXJjZSkKcmlib19maWxlIDwtIHBhc3RlMChpbnB1dF9kaXIsICcvJywgcmlib19zb3VyY2UpCnN0cmVzc19maWxlIDwtIHBhc3RlMChpbnB1dF9kaXIsICcvJywgc3RyZXNzX3NvdXJjZSkKZ2xfZmlsZXMgPC0gYyhtaXRvX2ZpbGUsIHJpYm9fZmlsZSwgc3RyZXNzX2ZpbGUpCgojIyBEb3dubG9hZCB0aGUgZmlsZSBmcm9tIFplbm9kbwppZiAoIWxvY2FsKSB7CiAgCiAgIyMjIFplbklECiAgemVuX2lkIDwtICIxNDAzNzM1NSIKICAjIyMgTG9vcGluZyBvbiBmaWxlcwogIGZvciAoZ2xmIGluIHNlcV9hbG9uZyhnbF9zb3VyY2VzKSkgewogICAgIyMjIFplbiBQYXRoCiAgICB6ZW5fYmFja3VwX2ZpbGUgPC0gcGFzdGUwKCJodHRwczovL3plbm9kby5vcmcvcmVjb3Jkcy8iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB6ZW5faWQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIvZmlsZXMvIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ2xfc291cmNlc1tnbGZdKQogICAgCiAgICAjIyBEb3dubG9hZCB0aGUgZmlsZQogICAgZG93bmxvYWQuZmlsZSh1cmwgPSB6ZW5fYmFja3VwX2ZpbGUsCiAgICAgICAgICAgICAgICAgIGRlc3RmaWxlID0gZ2xfZmlsZXNbZ2xmXSkKICB9CiAgcm0oZ2xfZmlsZXMpCn0gZWxzZSB7ICAjIyBMb2NhbCBtb2RlCiAgZWJhaWlfc2Vzc2lvbiA8LSAnMjUzOF9lYjNpX24xXzIwMjUnCiAgbG9jYWxiYWNrdXBfZGlyIDwtIHBhc3RlMCgnL3NoYXJlZC9wcm9qZWN0cy8nLCBlYmFpaV9zZXNzaW9uLCAnL2F0ZWxpZXJfc2NybmFzZXEvVEQvUkVTT1VSQ0VTL0dFTkVMSVNUUy8nKQogIG1pdG9fZmlsZSA8LSBwYXN0ZTAobG9jYWxiYWNrdXBfZGlyLCAnL211c19tdXNjdWx1c19taXRvX3N5bWJvbHNfMjAxOTEwMTUucmRzJykKICByaWJvX3NvdXJjZSA8LSBwYXN0ZTAoaW5wdXRfZGlyLCAnL211c19tdXNjdWx1c19jcmlib19zeW1ib2xzXzIwMTkxMDE1LnJkcycpCiAgc3RyZXNzX3NvdXJjZSA8LSBwYXN0ZTAoaW5wdXRfZGlyLCAnL211c19tdXNjdWx1c19zdHJlc3Nfc3ltYm9sc18yMDIwMDIyNC5yZHMnKQp9CmBgYAoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCgoKCgoKCgoKLS0tCgoqKkRlc2NyaXB0aW9uKio6CgpUaGlzIGZpbGUgZGVzY3JpYmVzIHRoZSBkaWZmZXJlbnQgc3RlcHMgdG8gdGhlIHF1YWxpdHkgY29udHJvbCBiYXNlZCBvbjoKCiogdGhlIG51bWJlciBvZiBVTUkgKHRyYW5zY3JpcHRzKSBkZXRlY3RlZCBwZXIgY2VsbAoqIHRoZSBudW1iZXIgb2YgZ2VuZXMgZGV0ZWN0ZWQgcGVyIGNlbGwKKiB0aGUgcHJvcG9ydGlvbiBvZiB0cmFuc2NyaXB0cyByZWxhdGVkIHRvIHRoZSBnZW5lcyBlbmNvZGVkIGluIHRoZSBtaXRvY2hvbmRyaWEsIHBlciBjZWxsCiogdGhlIHByb3BvcnRpb24gb2YgdHJhbnNjcmlwdHMgcmVsYXRlZCB0byB0aGUgZ2VuZXMgZW5jb2Rpbmcgcmlib3NvbWFsIHVuaXRzLCBwZXIgY2VsbAoqIHRoZSBwcm9wb3J0aW9uIG9mIHRyYW5zY3JpcHRzIHJlbGF0ZWQgdG8gc3RyZXNzIHNpZ25hdHVyZSwgcGVyIGNlbGwKCioqSW5wdXQgZGF0YSoqOiBTZXVyYXQgb2JqZWN0IGNvbnRhaW5pbmcgYWxsIGNlbGxzIChmaWx0ZXJlZCBjb3VudCBtYXRyaXgpCgoqKk91dHB1dCBkYXRhKio6IFNldXJhdCBvYmplY3QgYW5ub3RhdGVkIGZvciB0aGUgY2VsbHMgdG8gZmlsdGVyIG91dCBmb3IgZG93bnN0cmVhbSBhbmFseXNpcywgY2FsbGVkIGBzb2JqX1REM0FfcWNfYW5ub3RhdGVkLnJkc2AKCioqTmV4dCBzdGVwcyoqOiBDZWxscyBhbm5vdGF0aW9uIGZvciBjZWxsIHR5cGUsIGRvdWJsZXQgc3RhdHVzIGFuZCBjZWxsIGN5Y2xlIHN0YXR1cwoKIyBDb250ZXh0CgpJbiB0aGlzIGZpbGUsIHdlIHVzZWQgYSBkYXRhc2V0IGZyb20gdGhlIFtQYWl2YSBldCBhbC5dKGh0dHBzOi8vZmVicy5vbmxpbmVsaWJyYXJ5LndpbGV5LmNvbS9kb2kvZnVsbC8xMC4xMTExL2ZlYnMuMTQ2NTEpIHB1YmxpY2F0aW9uLgoKVGhlIHN0dWR5IGNvbmNlcm5zICoqdGh5bXVzIGF1dG9ub215Kio6CgoqIFRoZSB0aHltdXMgaXMgYW4gIm9yZ2FuIG9mIHBhc3NhZ2UiLCBjcml0aWNhbCBpbiBpdHMgZnVuY3Rpb24gdG8gdGhlIGFkYXB0aXZlIGltbXVuZSBzeXN0ZW0gZm9yIHRoZSBtYXR1cmF0aW9uIG9mIFQgY2VsbCBseW1waG9jeXRlcy4KKiBUaGlzIG1hdHVyYXRpb24gaW52b2x2ZXMgdHdvIG1haW4gc3RlcHMsIHBlcmZvcm1lZCB0aGFua3MgdG8gbWFjcm9waGFnZXM6CiAgKiBQb3NpdGl2ZSBzZWxlY3Rpb24gOiBrZWVwaW5nIGNlbGxzIHRoYXQgc3VjY2Vzc2Z1bGx5IGRldmVsb3AgcmVhY3QgYXBwcm9wcmlhdGVseSB3aXRoIE1IQyBpbW11bmUgcmVjZXB0b3JzIG9mIHRoZSBib2R5CiAgKiBOZWdhdGl2ZSBzZWxlY3Rpb24gOiBrZWVwaW5nIGNlbGxzIHRoYXQgZG8gbm90IHJlYWN0IGFnYWluc3QgbmF0dXJhbCBwcm90ZWlucyBvZiB0aGUgYm9keS4KKiBUaHltdXMgX2F1dG9ub215XyBpcyBhIG5hdHVyYWwgbWVjaGFuaXNtIHRoYXQgYWxsb3dzIHRvIGNyZWF0ZSBUIGNlbGxzIGluIHRoZSB0aHltdXMgYnkgZGlmZmVyZW50aWF0aW9uIGFuZCBjZWxsIGNvbXBldGl0aW9uLCBldmVuIHdoZW4gbm9ybWFsIHByb2dlbml0b3JzIGZyb20gdGhlIGJvbmUgbWFycm93IGFyZSBsYWNraW5nLCBpbiBjcml0aWNhbCBjb25kaXRpb25zLgoqIFRoaXMgbWVjaGFuaXNtIGlzIGtub3duIGluIGl0cyBlZmZlY3RzLCBidXQgdGhlIGNlbGxzIGludm9sdmVkIGluIGFyZSBub3QuCiogVGhpcyBzdHVkeSBpcyBvZiBpbXBvcnRhbmNlIGluIHRoZSBoZWFsdGggZmllbGQsIGFzIHRoaXMgbWVjaGFuaXNtIHJlbGllcyBvbiBhIHRlbXBvcmFyeSBsb3NzIG9mIGNvbnRyb2wgb2YgdGhlIGNlbGwgbm9ybWFsIGZ1bmN0aW9ucy4KKiBUaGUgY29uc2VxdWVuY2UgaXMgdGhhdCBpZiB0aHltdXMgaXMgaW4gYXV0b25vbXkgZm9yIHRvbyBsb25nIChmZXcgd2Vla3MpLCB0aGlzIGlzIGEgcHJlbHVkZSBmb3IgbGV1a2VtaWEgIQoKICA8Y2VudGVyPiFbXSh0aHltdXNfYXV0b25vbXkucG5nKTwvY2VudGVyPgoKKiBPcmdhbmlzbSBpcyA6ICoqbXVzIG11c2N1bHVzKioKKiBJbmRpdmlkdWFscyBhcmUgOiBtaWNlICoqaW4gZGV2ZWxvcG1lbnQsIGdyYWZ0ZWQqKgoqIFRoZSBkZXNpZ24gY29ycmVzcG9uZHMgdG8gKip0d28gY29uZGl0aW9ucyoqIChUZXN0IC8gY29udHJvbCkKICAqIENvbnRyb2wgOiB0aHltdXMgZnJvbSAqKndpbGQgdHlwZSoqIG5ld2Jvcm4gbW91c2UgdHJhbnNwbGFudGVkIGludG8gKip3aWxkIHR5cGUqKiBqdXZlbmlsZSBtb3VzZS4gSW4gdGhpcyBjb250cm9sIGNhc2UsICoqZG9ub3IqKiBULWNlbGxzIHByb2dlbml0b3JzIChETjMpIHdlcmUgcmVwbGFjZWQgYnkgKipob3N0KiogY2VsbHMgKiozIHdlZWtzKiogYWZ0ZXIgdHJhbnNwbGFudGF0aW9uLgogICogVGVzdCA6IHRoeW11cyBmcm9tICoqd2lsZCB0eXBlKiogbmV3Ym9ybiBtb3VzZSB0cmFuc3BsYW50ZWQgaW4gKipLTyBSYWctLy0qKiB0eXBlIGp1dmVuaWxlIG1vdXNlICh0aGUgS08gcGFydGlhbGx5IGltcGFpcnMgdGhlaXIgYWJpbGl0eSB0byBwcm9kdWNlIFQtY2VsbCBwcm9nZW5pdG9ycyBpbiBub3JtYWwgYW1vdW50cykuIEluIHRoaXMgdGVzdCBjYXNlLCAqKmRvbm9yKiogVC1jZWxscyBwcm9nZW5pdG9ycyAoRE4zKSB3ZXJlIHJlcGxhY2VkIGJ5ICoqaG9zdCoqIGNlbGxzICoqOSB3ZWVrcyoqIGFmdGVyIHRyYW5zcGxhbnRhdGlvbiwgc2hvd2luZyB0aGF0IHRoZSBkb25vciBETjMgY2VsbHMgb3V0bGl2ZWQgdGhlaXIgbm9ybWFsIGxpZmVzcGFuIGJ5IH42IHdlZWtzLgogIAogIDxjZW50ZXI+IVtdKHBhaXZhX3d0X2tvLnBuZyk8L2NlbnRlcj4KICAKCllvdSB3aWxsIG1haW5seSB3b3JrIG9uIHRoZSAqKktPIHNhbXBsZSAoJ1REM0EnKSoqLiBUaGUgaW5wdXQgZGF0YSBjb25zaXN0cyBpbiBhICoqY291bnQgbWF0cml4KiosIGFzIGEgZ3ppcHBlZCB0YWJ1bGFyIHRleHQgZmlsZSwgdGhhdCBjb250YWlucyBldmVyeXRoaW5nIG5lZWRlZCB0byBjcmVhdGUgYSBiYXNpYyBTZXVyYXQgb2JqZWN0IDoKICAqIFRoZSBleHByZXNzaW9ucyAqKmNvdW50cyoqCiAgKiBUaGUgKipmZWF0dXJlIG5hbWVzKiogKGhlcmUsIGdlbmUgc3ltYm9scykKICAqIFRoZSAqKmJhcmNvZGUgbmFtZXMqKgoKVGhpcyBtYXRyaXggaGFzIGFscmVhZHkgYmVlbiAqKmZpbHRlcmVkIGZvciBlbXB0eSBkcm9wbGV0cyoqLgoKPCEtLSAjIFByZS1yZXF1aXNpdGVzIC0tPgoKPCEtLSBUbyBvYnRhaW4gdGhpcyBmaWxlIGFuZCBzdGFydHMgdGhlIFRELCBjb3B5LXBhc3RlIHRoZSBmb2xkZXIgaW4geW91ciBwcm9qZWN0LCBlaXRoZXIgdXNpbmcgdGhlIGdyYXBoaWNhbCBpbnRlcmZhY2UsIG9yIGluIHRoZSBUZXJtaW5hbDogLS0+CgoKCjwhLS0gVGhlIHRyZWUgc2hvdWxkIGxvb2tzIGxpa2U6IC0tPgoKPCEtLSBgYGB7YmFzaH0gLS0+CjwhLS0gbHMgLS0+CjwhLS0gYGBgIC0tPgoKIyBFbnZpcm9ubWVudAoKV2UgbG9hZCB0aGUgcGFja2FnZSBvZiBpbnRlcmVzdDoKCmBgYHtyIHBhY2thZ2VzLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoU2V1cmF0KQoKLmxpYlBhdGhzKCkKYGBgCgojIERhdGEKCldlIGxvYWQgdGhlIGNvdW50IG1hdHJpeDoKCmBgYHtyIGxvYWRfY291bnRfbWF0cml4fQojICMgU2V0IGlucHV0IGRhdGEKIyBpbnB1dF9kYXRhID0gIi9zaGFyZWQvcHJvamVjdHMvMjUzOF9lYjNpX24xXzIwMjUvYXRlbGllcl9zY3JuYXNlcS9URC9CQUNLVVAvVFNWL0dTTTQ4NjExOTRfZ2V4XzJfcmF3X2dlbmVfZXhwcmVzc2lvbi50c3YuZ3oiCiMgCiMgIyBMb2FkIGRhdGEKIyBtYXQgPSByZWFkLnRhYmxlKGlucHV0X2RhdGEsCiMgICAgICAgICAgICAgICAgICBoZWFkZXIgPSBUUlVFLAojICAgICAgICAgICAgICAgICAgc2VwID0gIlx0IikKIyBtYXQgPSBhcy5tYXRyaXgobWF0KQojIG1hdCA9IE1hdHJpeDo6TWF0cml4KG1hdCwKIyAgICAgICAgICAgICAgICAgICAgICBzcGFyc2UgPSBUUlVFKQoKZGltKHNjbWF0KQpzY21hdFtjKDE6NSksIGMoMTo1KV0KYGBgCgpXZSBidWlsZCBhIFNldXJhdCBvYmplY3QgZnJvbSB0aGUgY291bnQgbWF0cml4OgoKYGBge3IgbWFrZV9zb2JqfQpzb2JqID0gU2V1cmF0OjpDcmVhdGVTZXVyYXRPYmplY3QoY291bnRzID0gc2NtYXQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhc3NheSA9ICJSTkEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJvamVjdCA9ICJURDNBIikKc29iagpgYGAKCldlIGRvIG5vdCBuZWVkIHRoZSBjb3VudCBtYXRyaXg6CgpgYGB7ciBybV9tYXR9CnJtKHNjbWF0KQpgYGAKCgo8IS0tICMgR2xvYmFsIHNldHRpbmdzIC0tPgoKPCEtLSBXZSBkZWZpbmUgdGhlIG91dHB1dCBkaXJlY3RvcnkgdG8gc2F2ZSB0aGUgU2V1cmF0IG9iamVjdCBpbiB0aGlzIGVuZCBvZiB0aGlzIGZpbGU6IC0tPgoKPCEtLSBgYGB7ciBvdXRkaXIsIGV2YWw9RkFMU0V9IC0tPgo8IS0tIG91dGRpciA9ICIuL2RhdGEiIC0tPgoKPCEtLSBpZiAoIWRpci5leGlzdHMob3V0ZGlyKSkgeyAtLT4KPCEtLSAgIGRpci5jcmVhdGUob3V0ZGlyLCByZWN1cnNpdmUgPSBGQUxTRSkgLS0+CjwhLS0gfSAtLT4KPCEtLSBgYGAgLS0+CgpXZSBzZXQgdGhlIGZpbHRlcmluZyB0aHJlc2hvbGRzIGJhc2VkIG9uIHF1YWxpdHkgY29udHJvbC1yZWxhdGVkIG1ldHJpY3MuIEFkanVzdCB0aGVtIGFzIG5lY2Vzc2FyeSBiYXNlZCBvbiB0aGUgZmlndXJlcy4KCmBgYHtyIHNldF90aHJlc2hvbGRzfQpjdXRfbkNvdW50X1JOQSA9IDMwMApjdXRfbG9nMTBfbkNvdW50X1JOQSA8LSAzCmN1dF9uRmVhdHVyZV9STkEgPSA3NTAKY3V0X3BlcmNlbnRfbXQgPSA1CmN1dF9wZXJjZW50X3JiID0gMzAKY3V0X3BlcmNlbnRfc3QgPSA2CmBgYAoKV2UgZGVmaW5lIGEgbmljZSBwYWxldHRlIHRvIHZpc3VhbGl6ZSB0aGUgUUMgbWV0cmljczoKCmBgYHtyIGNvbG9yX3BhbGV0dGV9CmNvbG9yX3BhbGV0dGUgPSBjKCJsaWdodGdyYXkiLCAiI0ZEQkI4NCIsICIjRUY2NTQ4IiwgIiM3RjAwMDAiLCAiYmxhY2siKQpgYGAKCgpGb3IgdGhlIFFDIG1ldHJpY3MgcmVsYXRlZCB0byB0aGUgcHJvcG9ydGlvbiBvZiBVTUkgYmVsb25ncyB0byBhIHNwZWNpZmljIGdlbmUgc2V0cywgd2UgbmVlZCB0byBsb2FkIHRoZSBnZW5lIHNldHMuCgpgYGB7ciBsb2FkX2xpc3RzfQptaXRvX3N5bWJvbHMgPSByZWFkUkRTKG1pdG9fZmlsZSkKbWl0b19zeW1ib2xzCgpyaWJvX3N5bWJvbHMgPSByZWFkUkRTKHJpYm9fZmlsZSkKcmlib19zeW1ib2xzCgpzdHJlc3Nfc3ltYm9scyA9IHJlYWRSRFMoc3RyZXNzX2ZpbGUpCnN0cmVzc19zeW1ib2xzCmBgYAoKV2Uga2VlcCBvbmx5IHRoZSBnZW5lIHN5bWJvbHMgYXZhaWxhYmxlIGluIG91ciBkYXRhOgoKYGBge3Igc3Vic2V0X3N5bWJvbHN9Cm1pdG9fc3ltYm9scyA9IGludGVyc2VjdChtaXRvX3N5bWJvbHMsIHJvd25hbWVzKHNvYmopKQptaXRvX3N5bWJvbHMKCnJpYm9fc3ltYm9scyA9IGludGVyc2VjdChyaWJvX3N5bWJvbHMsIHJvd25hbWVzKHNvYmopKQpyaWJvX3N5bWJvbHMKCnN0cmVzc19zeW1ib2xzID0gaW50ZXJzZWN0KHN0cmVzc19zeW1ib2xzLCByb3duYW1lcyhzb2JqKSkKc3RyZXNzX3N5bWJvbHMKYGBgCgoqKk5vdGUqKjogQSBtb3JlIHJvYnVzdCBhbmFseXNpcyBtYXkgdXNlIHRoZSBnZW5lIGlkZW50aWZpZXJzIGluc3RlYWQgb2YgZ2VuZSBzeW1ib2xzLgoKIyBGYXN0LXByb2Nlc3NpbmcKClRvIHZpc3VhbGl6ZSB0aGUgbG93IHF1YWxpdHkgY2VsbHMsIHdlIGdlbmVyYXRlIGEgcHJvamVjdGlvbi4gVW5kZXJzdGFuZGluZyB0aGUgY29tbWFuZHMgYmVsb3cgaXMgdGhlIHB1cnBvc2Ugb2YgbmV4dCBjb3Vyc2Ugc2Vzc2lvbnMuIFNvIGp1c3QgcnVuOgoKYGBge3IgcXVpY2tfcHJvY2Vzc2luZywgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0Kc29iaiA9IFNldXJhdDo6Tm9ybWFsaXplRGF0YShzb2JqKQpzb2JqID0gU2V1cmF0OjpTY2FsZURhdGEoc29iaikKc29iaiA9IFNldXJhdDo6RmluZFZhcmlhYmxlRmVhdHVyZXMoc29iaikKc29iaiA9IFNldXJhdDo6UnVuUENBKHNvYmopCnNvYmogPSBTZXVyYXQ6OlJ1blVNQVAoc29iaiwgZGltcyA9IGMoMToyMCkpCgpzb2JqCmBgYAoKV2UgY2FuIG5vdyB2aXN1YWxpemUgdGhlIGNlbGxzIG9uIGEgMkQgcHJvamVjdGlvbjoKCmBgYHtyIHZpc3VhbGl6ZV9jZWxscywgZmlnLndpZHRoID0gOCwgZmlnLmhlaWdodCA9IDh9ClNldXJhdDo6RGltUGxvdChzb2JqLAogICAgICAgICAgICAgICAgcmVkdWN0aW9uID0gInVtYXAiLAogICAgICAgICAgICAgICAgY29scyA9ICJibGFjayIpCmBgYAoKIyBRdWFsaXR5IGNvbnRyb2wKCiMjIENvbXB1dGUgbWV0cmljcwoKV2hhdCBpcyBhbHJlYWR5IGF2YWlsYWJsZSBpbiB0aGUgU2V1cmF0IG9iamVjdCA/CgpgYGB7ciBzZWVfbWV0YWRhdGF9CmhlYWQoc29iakBtZXRhLmRhdGEpCmBgYAoKSG93IGRvIHRoZSB0d28gZmlyc3QgUUMgbWV0cmljcyB2YXJ5ID8KCmBgYHtyIHN1bW1hcnlfbWV0YWRhdGF9CnN1bW1hcnkoc29iakBtZXRhLmRhdGEpCmBgYAoKSW4gdGhlIGNvbHVtbiBgbkNvdW50X1JOQWAsIHRoZSBtYXhpbXVtIGlzIGZhciBmcm9tIHRoZSB0aGlyZCBxdWFydGlsZS4gRm9yIHZpc3VhbGl6YXRpb24gcHVycG9zZSwgd2UgdHJhbnNmb3JtIHRoaXMgY29sdW1uIHRvIGxvZzEwIHNjYWxlLgoKYGBge3IgbG9nMTBfbmNvdW50X1JOQX0Kc29iaiRsb2cxMF9uQ291bnRfUk5BID0gbG9nMTAoc29iaiRuQ291bnRfUk5BKQoKc3VtbWFyeShzb2JqQG1ldGEuZGF0YSkKYGBgCgpXZSBjb21wdXRlIHRoZSBwZXJjZW50YWdlIG9mIFVNSSByZWxhdGVkIGZvciBlYWNoIG9mIHRoZSB0aHJlZSBsaXN0IG9mIGdlbmVzLiBGaXJzdCwgd2UgY29tcHV0ZSB0aGUgcHJvcG9ydGlvbiBvZiB0cmFuc2NyaXB0cyByZWxhdGVkIHRvIHRoZSBnZW5lcyBlbmNvZGVkIGluIHRoZSBtaXRvY2hvbmRyaWEsIHBlciBjZWxsLgoKYGBge3IgcGVyY2VudF9tdH0Kc29iaiA9IFNldXJhdDo6UGVyY2VudGFnZUZlYXR1cmVTZXQoCiAgc29iaiwKICBhc3NheSA9ICJSTkEiLAogIGZlYXR1cmVzID0gbWl0b19zeW1ib2xzLAogIGNvbC5uYW1lID0gInBlcmNlbnRfbXQiKQoKIyAjIEFsdGVybmF0aXZlIHdheSB0byBkbyAoYWxtb3N0KSB0aGUgc2FtZToKIyBzb2JqID0gU2V1cmF0OjpQZXJjZW50YWdlRmVhdHVyZVNldCgKIyAgIHNvYmosCiMgICBhc3NheSA9ICJSTkEiLAojICAgcGF0dGVybiA9ICJebXQtIiwKIyAgIGNvbC5uYW1lID0gInBlcmNlbnRfbXQiKQoKc3VtbWFyeShzb2JqJHBlcmNlbnRfbXQpCmBgYAoKVGhlbiwgd2UgY29tcHV0ZSB0aGUgcHJvcG9ydGlvbiBvZiB0cmFuc2NyaXB0cyByZWxhdGVkIHRvIHRoZSBnZW5lcyBlbmNvZGluZyByaWJvc29tYWwgdW5pdHMsIHBlciBjZWxsOgoKYGBge3IgcGVyY2VudF9yYn0Kc29iaiA9IFNldXJhdDo6UGVyY2VudGFnZUZlYXR1cmVTZXQoCiAgc29iaiwKICBhc3NheSA9ICJSTkEiLAogIGZlYXR1cmVzID0gcmlib19zeW1ib2xzLAogIGNvbC5uYW1lID0gInBlcmNlbnRfcmIiKQoKIyAjIEFsdGVybmF0aXZlIHdheSB0byBkbyAoYWxtb3N0KSB0aGUgc2FtZToKIyBzb2JqID0gU2V1cmF0OjpQZXJjZW50YWdlRmVhdHVyZVNldCgKIyAgIHNvYmosCiMgICBhc3NheSA9ICJSTkEiLAojICAgcGF0dGVybiA9ICJeUnBbbHxzXVthLXpdP1swLTldKlthLHhdPyQiLAojICAgY29sLm5hbWUgPSAicGVyY2VudF9yYiIpCgpzdW1tYXJ5KHNvYmokcGVyY2VudF9yYikKYGBgCgoKRmluYWxseSwgd2UgY29tcHV0ZSB0aGUgcHJvcG9ydGlvbiBvZiB0cmFuc2NyaXB0cyByZWxhdGVkIHRvIHN0cmVzcyBzaWduYXR1cmUsIHBlciBjZWxsOgoKYGBge3IgcGVyY2VudF9zdH0Kc29iaiA9IFNldXJhdDo6UGVyY2VudGFnZUZlYXR1cmVTZXQoCiAgc29iaiwKICBhc3NheSA9ICJSTkEiLAogIGZlYXR1cmVzID0gc3RyZXNzX3N5bWJvbHMsCiAgY29sLm5hbWUgPSAicGVyY2VudF9zdCIpCgpzdW1tYXJ5KHNvYmokcGVyY2VudF9zdCkKYGBgCgoKV2Uga25vdyBoYXZlIGFsbCB0aGUgUUMtcmVsYXRlZCBtZXRyaWNzOgoKYGBge3IgZmluYWxfc3VtbWFyeX0Kc3VtbWFyeShzb2JqQG1ldGEuZGF0YSkKYGBgCgojIyBGYWlsaW5nIGNlbGxzCgpXZSBpZGVudGlmeSB0aGUgY2VsbHMgdGhhdCBkbyBub3QgcGFzcyB0aGUgcXVhbGl0eSBjb250cm9sLiBUaGlzIHdpbGwgYmUgdXNlZCBmb3IgdGhlIHZpc3VhbGl6YXRpb24gYW5kIGZvciBmaWx0ZXJpbmcuIElmIHRoZSBmaWx0ZXJpbmcgdGhyZXNob2xkcyBhcmUgbW9kaWZpZWQsIGRvIG5vdCBmb3JnZXQgdG8gcnVuIGFnYWluIHRoaXMgY2h1bmsuCgpgYGB7ciBmYWlsX2NlbGxzfQpmYWlsX3BlcmNlbnRfbXQgPSBzb2JqQG1ldGEuZGF0YSAlPiUKICBkcGx5cjo6ZmlsdGVyKHBlcmNlbnRfbXQgPiBjdXRfcGVyY2VudF9tdCkgJT4lCiAgcm93bmFtZXMoKQoKZmFpbF9wZXJjZW50X3JiID0gc29iakBtZXRhLmRhdGEgJT4lCiAgZHBseXI6OmZpbHRlcihwZXJjZW50X3JiID4gY3V0X3BlcmNlbnRfcmIpICU+JQogIHJvd25hbWVzKCkKCmZhaWxfcGVyY2VudF9zdCA9IHNvYmpAbWV0YS5kYXRhICU+JQogIGRwbHlyOjpmaWx0ZXIocGVyY2VudF9zdCA+IGN1dF9wZXJjZW50X3N0KSAlPiUKICByb3duYW1lcygpCgpmYWlsX25Db3VudF9STkEgPSBzb2JqQG1ldGEuZGF0YSAlPiUKICBkcGx5cjo6ZmlsdGVyKG5Db3VudF9STkEgPCBjdXRfbkNvdW50X1JOQSkgJT4lCiAgcm93bmFtZXMoKQoKZmFpbF9uRmVhdHVyZV9STkEgPSBzb2JqQG1ldGEuZGF0YSAlPiUKICBkcGx5cjo6ZmlsdGVyKG5GZWF0dXJlX1JOQSA8IGN1dF9uRmVhdHVyZV9STkEpICU+JQogIHJvd25hbWVzKCkKYGBgCgojIyBWaXN1YWxpemF0aW9uCgpUaGlzIGlzIGRpZmZpY3VsdCB0byBoYW5kbGUgdGhlIGRpc3RyaWJ1dGlvbiBvZiB0aGVzZSBtZXRyaWNzIGFjcm9zcyBjZWxscy4gV2Ugb3B0IGZvciB2YXJpb3VzIHZpc3VhbGl6YXRpb24gd2F5czoKCiogKipoaXN0b2dyYW0qKiwgc2hvd2luZyB0aGUgZGlzdHJpYnV0aW9uIG9mIHRoZSBtZXRyaWMKKiAqKnZpb2xpbi9ib3ggcGxvdCoqLCBzaG93aW5nIHRoZSBkaXN0cmlidXRpb24gb2YgdGhlIG1ldHJpYywgdXNlZnVsIGlmIHNldmVyYWwgZGF0YXNldHMgYXJlIGNvbnNpZGVyZWQKKiAqKlVNQVAqKiAob3IgYWx0ZXJuYXRpdmVseSwgdFNORSksIHNob3dpbmcgdGhlIGRpc3RyaWJ1dGlvbiBvZiB0aGUgbWV0cmljIG92ZXIgYSAyRCBwcm9qZWN0aW9uIG9mIGNlbGxzCgpZb3UgbWF5IGNob29zZSBvbmUgb2YgdGhlc2UgdmlzdWFsaXphdGlvbiB3YXlzLgoKIyMjIE51bWJlciBvZiBVTUkKCiMjIyMgRGVmaW5lIHRoZSBjb2RlCgpXZSB3cml0ZSBhIGxvdCBvZiBjb2RlIHRvIGNyZWF0ZSBvdXIgZmlndXJlcyBvZiBpbnRlcmVzdDoKCmBgYHtyIGRlZmluZV9jb2RlLCBmaWcud2lkdGggPSAxOCwgZmlnLmhlaWdodCA9IDR9CiMgSGlzdG9ncmFtIHNob3dpbmc6CiMgLSB0aGUgZGlzdHJpYnV0aW9uIG9mIHRoZSBtZXRyaWMKIyAtIGFuIGVzdGltYXRlZCBkZW5zaXR5CiMgLSB0aGUgZmlsdGVyaW5nIHRocmVzaG9sZCBhcyBhIHN0cmFpZ2h0IGxpbmUKcF9oaXN0ID0gZ2dwbG90KHNvYmpAbWV0YS5kYXRhLCBhZXMoeCA9IGxvZzEwX25Db3VudF9STkEpKSArCiAgZ2VvbV9oaXN0b2dyYW0oYWVzKHkgPSBhZnRlcl9zdGF0KGRlbnNpdHkpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvdXIgPSAiYmxhY2siLCBmaWxsID0gIiNGODc2NkQiLCBiaW5zID0gMTAwKSArCiAgZ2VvbV9kZW5zaXR5KGFscGhhID0gMCwgY29sID0gImJsdWUiLCBsd2QgPSAwLjc1KSArCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gY3V0X2xvZzEwX25Db3VudF9STkEsIGNvbCA9ICJyZWQiKSArCiAgbGFicyh0aXRsZSA9IHBhc3RlMCgiVGhyZXNob2xkIGZvciBsb2cxMF9uQ291bnRfUk5BIGlzOiAiLCBjdXRfbG9nMTBfbkNvdW50X1JOQSkpICsKICB0aGVtZV9jbGFzc2ljKCkgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKQoKIyBWaW9saW4gcGxvdDoKIyAtIGlzIGVhc2lseSBhY2Nlc3NpYmxlIGluIHRoZSBTZXVyYXQgcGFja2FnZQojIC0gY2FuIG9yIG5vdCBkaXNwbGF5IHRoZSBjZWxscyAoc2V0IGBwdC5zaXplID0gMGAgdG8gaGlkZSB0aGUgY2VsbHMpCiMgLSBpcyB1c2VmdWwgd2hlbiBzZXZlcmFsIGRhdGFzZXRzIGhhdmUgYmVlbiBtZXJnZWQKcF92aW9saW4gPSBTZXVyYXQ6OlZsblBsb3Qoc29iaiwgZmVhdHVyZXMgPSAibG9nMTBfbkNvdW50X1JOQSIpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSBjdXRfbG9nMTBfbkNvdW50X1JOQSwgY29sID0gInJlZCIpICsKICB0aGVtZShheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQoKIyAjIEJveCBwbG90IGlzIHNpbWlsYXIgdG8gdmlvbGluIHBsb3QgYnV0IG5vdCBpbiB0aGUgU2V1cmF0IHBhY2thZ2UKIyBwX2JveHBsb3QgPSBnZ3Bsb3Qoc29iakBtZXRhLmRhdGEsIGFlcyh5ID0gbG9nMTBfbkNvdW50X1JOQSwKIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB4ID0gb3JpZy5pZGVudCkpICsKIyAgIGdlb21fYm94cGxvdChjb2xvdXIgPSAiYmxhY2siLCBmaWxsID0gIiNGODc2NkQiKSArCiMgICBnZW9tX2ppdHRlcih3aWR0aCA9IDAuMywgc2l6ZSA9IDAuMDAxKSArCiMgICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSBjdXRfbG9nMTBfbkNvdW50X1JOQSwgY29sID0gInJlZCIpICsKIyAgIHRoZW1lX2NsYXNzaWMoKSArCiMgICB0aGVtZShheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCkpCgojIFRoaXMgMkQgcmVwcmVzZW50YXRpb24gc2hvd3MgdGhlIGRpc3RyaWJ1dGlvbiBvZiB0aGUgbWV0cmljIG92ZXIgY2VsbHMKcF91bWFwID0gRmVhdHVyZVBsb3Qoc29iaiwKICAgICAgICAgICAgICAgICAgICAgcmVkdWN0aW9uID0gInVtYXAiLAogICAgICAgICAgICAgICAgICAgICBmZWF0dXJlcyA9ICJsb2cxMF9uQ291bnRfUk5BIikgKwogIHNjYWxlX2NvbG9yX2dyYWRpZW50bihjb2xvcnMgPSBjb2xvcl9wYWxldHRlKSArCiAgdGhlbWUoYXNwZWN0LnJhdGlvID0gMSkKCiMgVGhpcyAyRCByZXByZXNlbnRhdGlvbiBzaG93cyB0aGUgbG93IHF1YWxpdHkgY2VsbHMgaW4gY29sb3IKIyBgb3JkZXIgPSAiZmFpbCJgIGlzIHVzZWQgdG8gZGlzcGxheSBmYWlsaW5nIGNlbGxzIG9uIGZyb250CiMgYmVmb3JlLCB3ZSBuZWVkIHRvIGRlZmluZSB0aGUgImZhaWxvcnBhc3MiIGNvbHVtbiBpbiBzb2JqQG1ldGEuZGF0YQojIEl0IGNvcnJlc3BvbmRzIHRvICJmYWlsIiBmb3IgY2VsbHMgdGhhdCBkbyBubyBwYXNzIHRoZSB0aHJlc2hvbGQsCiMgb3IgInBhc3MiIG90aGVyd2lzZQpzb2JqJGZhaWxvcnBhc3MgPSBpZmVsc2UoY29sbmFtZXMoc29iaikgJWluJSBmYWlsX25Db3VudF9STkEsCiAgICAgICAgICAgICAgICAgICAgICAgICB5ZXMgPSAiZmFpbCIsIG5vID0gInBhc3MiKSAlPiUKICBhcy5mYWN0b3IoKQoKcF9mYWlsID0gRGltUGxvdChzb2JqLAogICAgICAgICAgICAgICAgIGdyb3VwLmJ5ID0gImZhaWxvcnBhc3MiLAogICAgICAgICAgICAgICAgIG9yZGVyID0gImZhaWwiKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoIiNGODc2NkQiLCAiZ3JheTgwIiksCiAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IGxldmVscyhzb2JqJGZhaWxvcnBhc3MpKSArCiAgbGFicyh0aXRsZSA9ICJsb2cxMF9uQ291bnRfUk5BIiwKICAgICAgIHN1YnRpdGxlID0gcGFzdGUwKGxlbmd0aChmYWlsX25Db3VudF9STkEpLCAiIGNlbGxzIGZhaWwgKCIsCiAgICAgICAgICAgICAgICAgICAgICAgICByb3VuZCgxMDAqbGVuZ3RoKGZhaWxfbkNvdW50X1JOQSkvbmNvbChzb2JqKSwgMiksICIgJSkiKSkgKwogIHRoZW1lKGFzcGVjdC5yYXRpbyA9IDEsCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSksCiAgICAgICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpCgpzb2JqJGZhaWxvcnBhc3MgPSBOVUxMICMgcmVtb3ZlIHRoZSBjb2x1bW4gKGl0IHdhcyB0ZW1wb3JhcnkpCgojIFdlIHVzZSB0aGUgcGF0Y2h3b3JrIHBhY2thZ2UgdG8gYXJyYW5nZSBhbGwgZmlndXJlcyB0b2dldGhlcgpwYXRjaHdvcms6OndyYXBfcGxvdHMocF91bWFwLCBwX2ZhaWwsIHBfaGlzdCwgcF92aW9saW4pICsKICBwYXRjaHdvcms6OnBsb3RfbGF5b3V0KG5yb3cgPSAxLCB3aWR0aHMgPSBjKDEsIDEsIDIsIDEpKQpgYGAKCiMjIyMgRGVmaW5lIGEgZnVuY3Rpb24KCkluc3RlYWQgb2YgY29weWluZy1wYXN0aW5nIHRoaXMgc2VjdGlvbiBvZiBjb2RlIGZvciBhbGwgUUMtbWV0cmljcywgd2UgZGVzaWduIGEgZnVuY3Rpb24gdXNpbmcgdGhlIHRlbXBsYXRlIGJlbG93OgoKYGBge3IgbXlfZnVuY3Rpb25fbmFtZSwgZXZhbCA9IEZBTFNFfQpteV9mdW5jdGlvbl9uYW1lID0gZnVuY3Rpb24ocGFyYW0xLCBwYXJhbTIpIHsKICAjIGRvIHNvbWV0aGluZyB3aXRoIHRoZSBwYXJhbWV0ZXIgdmFsdWVzCiAgb3V0cHV0ID0gInNvbWV0aGluZyIKICAKICByZXR1cm4ob3V0cHV0KQp9CmBgYAoKSGVyZSBpcyB0aGUgZnVuY3Rpb246CgpgYGB7ciBxY19wcmludF9mdW5jdGlvbn0KcHJpbnRfMV9xY19tZXRyaWMgPSBmdW5jdGlvbihvYmplY3QgPSBzb2JqLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHFjID0gImxvZzEwX25Db3VudF9STkEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGN1dF9xYyA9IGN1dF9sb2cxMF9uQ291bnRfUk5BLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZhaWxpbmdfY2VsbHMgPSBmYWlsX25Db3VudF9STkEpIHsKICAjIERlc2NyaXB0aW9uIG9mIHRoZSBwYXJhbWV0ZXJzOgogICMgLSBzb2JqIDogdGhlIFNldXJhdCBvYmplY3QsIHdpdGggZGVmYXVsdCB2YWx1ZSB0byB0aGUgb25lCiAgIyAtIHFjIDogQ0hBUkFDVEVSIDogdGhlIFFDIG1ldHJpYywgbXVzdCBiZSBhIGNvbHVtbiBpbiBzb2JqQG1ldGEuZGF0YQogICMgLSBjdXRfcWMgOiBOVU1FUklDIDogdGhlIGZpbHRlcmluZyB0aHJlc2hvbGQgZm9yIHRoZSBRQyBtZXRyaWMKICAjIC0gZmFpbGluZ19jZWxscyA6IENIQVJBQ1RFUiBWRUNUT1IgOiB0aGUgY2VsbHMgdGhhdCBmYWlsIHRoZSBRQwogIAogICMgSGlzdG9ncmFtCiAgcF9oaXN0ID0gZ2dwbG90KG9iamVjdEBtZXRhLmRhdGEsIGFlcyh4ID0gLmRhdGFbW3FjXV0pKSArCiAgICBnZW9tX2hpc3RvZ3JhbShhZXMoeSA9IGFmdGVyX3N0YXQoZGVuc2l0eSkpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3VyID0gImJsYWNrIiwgZmlsbCA9ICIjRjg3NjZEIiwgYmlucyA9IDEwMCkgKwogICAgZ2VvbV9kZW5zaXR5KGFscGhhID0gMCwgY29sID0gImJsdWUiLCBsd2QgPSAwLjc1KSArCiAgICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBjdXRfcWMsIGNvbCA9ICJyZWQiKSArCiAgICBsYWJzKHRpdGxlID0gcGFzdGUwKCJUaHJlc2hvbGQgZm9yICIsIHFjLCAiIGlzOiAiLCBjdXRfcWMpKSArCiAgICB0aGVtZV9jbGFzc2ljKCkgKwogICAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpCiAgCiAgIyBWaW9saW4gcGxvdAogIHBfdmlvbGluID0gU2V1cmF0OjpWbG5QbG90KG9iamVjdCwgZmVhdHVyZXMgPSBxYykgKwogICAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gY3V0X3FjLCBjb2wgPSAicmVkIikgKwogICAgdGhlbWUoYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQogIAogICMgQm94IHBsb3QKICBwX2JveHBsb3QgPSBnZ3Bsb3Qob2JqZWN0QG1ldGEuZGF0YSwgYWVzKHkgPSAuZGF0YVtbcWNdXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHggPSAib3JpZy5pZGVudCIpKSArCiAgICBnZW9tX2JveHBsb3QoY29sb3VyID0gImJsYWNrIiwgZmlsbCA9ICIjRjg3NjZEIikgKwogICAgZ2VvbV9qaXR0ZXIod2lkdGggPSAwLjMsIHNpemUgPSAwLjAwMSkgKwogICAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gY3V0X3FjLCBjb2wgPSAicmVkIikgKwogICAgdGhlbWVfY2xhc3NpYygpICsKICAgIHRoZW1lKGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSkKICAKICAjIEZlYXR1cmUgcGxvdAogIHBfdW1hcCA9IFNldXJhdDo6RmVhdHVyZVBsb3Qob2JqZWN0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVkdWN0aW9uID0gInVtYXAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmVhdHVyZXMgPSBxYykgKwogICAgc2NhbGVfY29sb3JfZ3JhZGllbnRuKGNvbG9ycyA9IGNvbG9yX3BhbGV0dGUpICsKICAgIHRoZW1lKGFzcGVjdC5yYXRpbyA9IDEpCiAgCiAgIyBEaW0gcGxvdAogIG9iamVjdCRmYWlsb3JwYXNzID0gaWZlbHNlKGNvbG5hbWVzKG9iamVjdCkgJWluJSBmYWlsaW5nX2NlbGxzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHllcyA9ICJmYWlsIiwgbm8gPSAicGFzcyIpICU+JQogICAgYXMuZmFjdG9yKCkKICAKICBwX2ZhaWwgPSBTZXVyYXQ6OkRpbVBsb3Qob2JqZWN0LAogICAgICAgICAgICAgICAgICAgICAgICAgICBncm91cC5ieSA9ICJmYWlsb3JwYXNzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgb3JkZXIgPSAiZmFpbCIpICsKICAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCIjRjg3NjZEIiwgImdyYXk4MCIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IGxldmVscyhvYmplY3QkZmFpbG9ycGFzcykpICsKICAgIGxhYnModGl0bGUgPSBxYywKICAgICAgICAgICAgICAgICAgc3VidGl0bGUgPSBwYXN0ZTAobGVuZ3RoKGZhaWxpbmdfY2VsbHMpLCAiIGNlbGxzIGZhaWwgKCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJvdW5kKDEwMCpsZW5ndGgoZmFpbGluZ19jZWxscykvbmNvbChzb2JqKSwgMiksICIgJSkiKSkgKwogICAgdGhlbWUoYXNwZWN0LnJhdGlvID0gMSwKICAgICAgICAgICAgICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpLAogICAgICAgICAgICAgICAgICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpCiAgCiAgIyBQYXRjaHdvcmsKICBwID0gcGF0Y2h3b3JrOjp3cmFwX3Bsb3RzKHBfdW1hcCwgcF9mYWlsLCBwX2hpc3QsIHBfdmlvbGluLCBwX2JveHBsb3QpICsKICAgIHBhdGNod29yazo6cGxvdF9sYXlvdXQobnJvdyA9IDEsIHdpZHRocyA9IGMoMSwgMSwgMiwgMSwgMSkpCiAgCiAgcmV0dXJuKHApCn0KYGBgCgojIyMjIENoZWNrIHRoZSBmdW5jdGlvbgoKVGhpcyBpcyB3b3JraW5nIGZvciBgbG9nMTBfbkNvdW50X1JOQWAsIGJlY2F1c2UgaXQgaXMgdXNlZCB0byBkZWZpbmUgZGVmYXVsdCBwYXJhbWV0ZXIgdmFsdWVzOgoKYGBge3IgY2hlY2sxX2xvZzEwX25Db3VudF9STkEsIGZpZy53aWR0aCA9IDE4LCBmaWcuaGVpZ2h0ID0gNH0KcHJpbnRfMV9xY19tZXRyaWMoKQpgYGAKCmJ1dCBhbHNvIHdvcmtzIGJ5IHNwZWNpZnlpbmcgdGhlIHBhcmFtZXRlciB2YWx1ZXM6CgpgYGB7ciBjaGVjazJfbG9nMTBfbkNvdW50X1JOQSwgZmlnLndpZHRoID0gMTgsIGZpZy5oZWlnaHQgPSA0fQpwcmludF8xX3FjX21ldHJpYyhzb2JqLAogICAgICAgICAgICAgICAgICBxYyA9ICJsb2cxMF9uQ291bnRfUk5BIiwKICAgICAgICAgICAgICAgICAgY3V0X3FjID0gY3V0X2xvZzEwX25Db3VudF9STkEsCiAgICAgICAgICAgICAgICAgIGZhaWxpbmdfY2VsbHMgPSBmYWlsX25Db3VudF9STkEpCmBgYAoKU28gd2UgY2FuIGNvcHktcGFzdGUgb25seSB0aGlzIGNodW5rIGZvciB0aGUgbmV4dCBRQyBtZXRyaWNzICEKCiMjIyBOdW1iZXIgb2YgZ2VuZXMKCmBgYHtyIHNlZV9uRmVhdHVyZV9STkEsIGZpZy53aWR0aCA9IDE4LCBmaWcuaGVpZ2h0ID0gNH0KcHJpbnRfMV9xY19tZXRyaWMoc29iaiwKICAgICAgICAgICAgICAgICAgcWMgPSAibkZlYXR1cmVfUk5BIiwKICAgICAgICAgICAgICAgICAgY3V0X3FjID0gY3V0X25GZWF0dXJlX1JOQSwKICAgICAgICAgICAgICAgICAgZmFpbGluZ19jZWxscyA9IGZhaWxfbkZlYXR1cmVfUk5BKQpgYGAKCiMjIyBNaXRvY2hvbmRyaWFsIGdlbmVzIGV4cHJlc3Npb24KCmBgYHtyIHNlZV9wZXJjZW50X210LCBmaWcud2lkdGggPSAxOCwgZmlnLmhlaWdodCA9IDR9CnByaW50XzFfcWNfbWV0cmljKHNvYmosCiAgICAgICAgICAgICAgICAgIHFjID0gInBlcmNlbnRfbXQiLAogICAgICAgICAgICAgICAgICBjdXRfcWMgPSBjdXRfcGVyY2VudF9tdCwKICAgICAgICAgICAgICAgICAgZmFpbGluZ19jZWxscyA9IGZhaWxfcGVyY2VudF9tdCkKYGBgCgojIyMgUmlib3NvbWFsIGdlbmVzIGV4cHJlc3Npb24KCmBgYHtyIHNlZV9wZXJjZW50X3JiLCBmaWcud2lkdGggPSAxOCwgZmlnLmhlaWdodCA9IDR9CnByaW50XzFfcWNfbWV0cmljKHNvYmosCiAgICAgICAgICAgICAgICAgIHFjID0gInBlcmNlbnRfcmIiLAogICAgICAgICAgICAgICAgICBjdXRfcWMgPSBjdXRfcGVyY2VudF9yYiwKICAgICAgICAgICAgICAgICAgZmFpbGluZ19jZWxscyA9IGZhaWxfcGVyY2VudF9yYikKYGBgCgojIyMgU3RyZXNzLXJlbGF0ZWQgZ2VuZXMgZXhwcmVzc2lvbgoKYGBge3Igc2VlX3BlcmNlbnRfc3QsIGZpZy53aWR0aCA9IDE4LCBmaWcuaGVpZ2h0ID0gNH0KcHJpbnRfMV9xY19tZXRyaWMoc29iaiwKICAgICAgICAgICAgICAgICAgcWMgPSAicGVyY2VudF9zdCIsCiAgICAgICAgICAgICAgICAgIGN1dF9xYyA9IGN1dF9wZXJjZW50X3N0LAogICAgICAgICAgICAgICAgICBmYWlsaW5nX2NlbGxzID0gZmFpbF9wZXJjZW50X3N0KQpgYGAKCiMgRmlsdGVyaW5nCgpXZSBjb3VsZCBmaWx0ZXIgb3V0IGNlbGxzIGJhc2VkIG9uIHRoZXNlIDUgUUMgbWV0cmljcyBub3cuIEl0IGlzIGFsc28gcG9zc2libGUgdG8gd2FpdCwgcGVyZm9ybSB2YXJpb3VzIGFubm90YXRpb25zIHN1Y2ggYXMgY2VsbCB0eXBlIGFubm90YXRpb24gb3IgY2VsbCBjeWNsZSBwaGFzZSBzY29yaW5nLCB0byBiZXR0ZXIgY2hhcmFjdGVyaXplIHRoZSBsb3cgcXVhbGl0eSBjZWxscy4KClRvIGZpbHRlciBhIFNldXJhdCBvYmplY3QsIHdlIHVzZSB0aGUgYHN1YnNldGAgZnVuY3Rpb246CgpgYGB7ciBmaWx0ZXJfc29ian0Kc29ial9maWx0ZXJlZCA9IHN1YnNldChzb2JqLCBpbnZlcnQgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgIGNlbGxzID0gdW5pcXVlKGMoZmFpbF9uQ291bnRfUk5BLCBmYWlsX25GZWF0dXJlX1JOQSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZhaWxfcGVyY2VudF9tdCwgZmFpbF9wZXJjZW50X3JiLCBmYWlsX3BlcmNlbnRfc3QpKSkKc29ial9maWx0ZXJlZApgYGAKCldlIGFyZSBnb2luZyB0byBzYXZlIHRoZSBvYmplY3QgYW5ub3RhdGVkIGZvciBjZWxscyB0aGF0IGZhaWwgdGhlIHF1YWxpdHkgY29udHJvbCwgcmVnYXJkbGVzcyB0aGUgbWV0cmljcy4gU28gd2UgYWRkIGEgc2luZ2xlIGNvbHVtbiB0byBgc29iakBtZXRhLmRhdGFgIDoKCmBgYHtyIGFkZF9mYWlsX3FjfQpzb2JqJGZhaWxfcWMgPSBpZmVsc2UodGVzdCA9IGNvbG5hbWVzKHNvYmopICVpbiUgY29sbmFtZXMoc29ial9maWx0ZXJlZCksCiAgICAgICAgICAgICAgICAgICAgICB5ZXMgPSAicGFzcyIsCiAgICAgICAgICAgICAgICAgICAgICBubyA9ICJmYWlsIikKCnRhYmxlKHNvYmokZmFpbF9xYykKYGBgCgoKIyBTYXZlCgpXZSBzYXZlIHRoZSBub24tZmlsdGVyZWQgU2V1cmF0IG9iamVjdDoKCmBgYHtyIHNhdmVfc29iaiwgZXZhbD1GQUxTRX0Kc2F2ZVJEUyhzb2JqLCBmaWxlID0gcGFzdGUwKG91dHB1dF9kaXIsICIvc29ial9URDNBX3FjX2Fubm90YXRlZC5yZHMiKSkKYGBgCgpUaGlzIFNldXJhdCBvYmplY3QgY2FuIHRoZW4gYmUgdGhlIGlucHV0IG9iamVjdCBmb3IgYW5ub3RhdGlvbiwgZGVmaW5pdGlvbiBvZiBhIG5ldyBwcm9qZWN0aW9uIGFuZCBkb3duc3RyZWFtIGFuYWx5c2VzLgoKIyBSIHNlc3Npb24KClRoaXMgaXMgYSBnb29kIHByYWN0aWNlIHRvIHNob3cgdGhlIHZlcnNpb24gb2YgdGhlIHBhY2thZ2VzIHVzZWQgaW4gdGhpcyBub3RlYm9vay4KCmBgYHtyIHNlc3Npb25pbmZvLCBjbGFzcy5zb3VyY2UgPSAiZm9sZC1oaWRlIn0Kc2Vzc2lvbkluZm8oKQpgYGAKCg==